baark 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,121 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+ workflow_dispatch:
8
+ workflow_call:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ concurrency:
14
+ group: ci-${{ github.event.pull_request.number || github.run_id }}
15
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
16
+
17
+ env:
18
+ UV_VERSION: "0.12.6"
19
+ JUST_VERSION: "1.58.0"
20
+
21
+ defaults:
22
+ run:
23
+ shell: bash
24
+
25
+ jobs:
26
+ quality:
27
+ name: Quality
28
+ runs-on: ubuntu-24.04
29
+ timeout-minutes: 5
30
+ steps:
31
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
32
+ with:
33
+ persist-credentials: false
34
+
35
+ - uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
36
+ with:
37
+ version: ${{ env.UV_VERSION }}
38
+ python-version: "3.13"
39
+ cache-dependency-glob: uv.lock
40
+
41
+ - uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
42
+ with:
43
+ just-version: ${{ env.JUST_VERSION }}
44
+
45
+ - name: Install actionlint
46
+ run: |
47
+ set -euo pipefail
48
+ curl --fail --silent --show-error --location \
49
+ https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz \
50
+ --output "$RUNNER_TEMP/actionlint.tar.gz"
51
+ echo "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8 $RUNNER_TEMP/actionlint.tar.gz" | sha256sum --check
52
+ tar -xzf "$RUNNER_TEMP/actionlint.tar.gz" -C "$RUNNER_TEMP" actionlint
53
+ echo "$RUNNER_TEMP" >> "$GITHUB_PATH"
54
+
55
+ - run: just lint lint-workflows
56
+
57
+ build:
58
+ name: Distributions
59
+ needs: quality
60
+ runs-on: ubuntu-24.04
61
+ timeout-minutes: 5
62
+ steps:
63
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
64
+ with:
65
+ persist-credentials: false
66
+
67
+ - uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
68
+ with:
69
+ version: ${{ env.UV_VERSION }}
70
+ python-version: "3.13"
71
+ cache-dependency-glob: uv.lock
72
+
73
+ - uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
74
+ with:
75
+ just-version: ${{ env.JUST_VERSION }}
76
+
77
+ - name: Build and validate release artifacts
78
+ env:
79
+ RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || '' }}
80
+ run: just build
81
+
82
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
83
+ with:
84
+ name: python-distributions
85
+ path: |
86
+ dist/*.whl
87
+ dist/*.tar.gz
88
+ if-no-files-found: error
89
+ retention-days: 14
90
+
91
+ test:
92
+ name: Wheel / ${{ matrix.os }} / Python ${{ matrix.python }}
93
+ needs: build
94
+ runs-on: ${{ matrix.os }}
95
+ timeout-minutes: 5
96
+ strategy:
97
+ fail-fast: false
98
+ matrix:
99
+ os: [ubuntu-24.04, macos-15, windows-2025]
100
+ python: ["3.13", "3.14"]
101
+ steps:
102
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
103
+ with:
104
+ persist-credentials: false
105
+
106
+ - uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
107
+ with:
108
+ version: ${{ env.UV_VERSION }}
109
+ python-version: ${{ matrix.python }}
110
+ cache-dependency-glob: uv.lock
111
+
112
+ - uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
113
+ with:
114
+ just-version: ${{ env.JUST_VERSION }}
115
+
116
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
117
+ with:
118
+ name: python-distributions
119
+ path: dist
120
+
121
+ - run: just test-wheel
@@ -0,0 +1,36 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: pypi-${{ github.event.release.tag_name }}
12
+ cancel-in-progress: false
13
+
14
+ jobs:
15
+ checks:
16
+ if: github.repository == 'ngc2000/baark'
17
+ uses: ./.github/workflows/ci.yml
18
+
19
+ publish:
20
+ name: Publish tested distributions
21
+ needs: checks
22
+ runs-on: ubuntu-24.04
23
+ timeout-minutes: 5
24
+ environment:
25
+ name: pypi
26
+ url: https://pypi.org/p/baark
27
+ permissions:
28
+ id-token: write
29
+ steps:
30
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
31
+ with:
32
+ name: python-distributions
33
+ path: dist
34
+
35
+ - name: Publish with Trusted Publishing
36
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
baark-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 doabell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
baark-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,240 @@
1
+ Metadata-Version: 2.4
2
+ Name: baark
3
+ Version: 0.1.0
4
+ Summary: Typed sync and async Python SDK for Bark push notifications
5
+ Keywords: bark,notifications,push,async,encryption
6
+ Author: doabell
7
+ Author-email: doabell <35297086+doabell@users.noreply.github.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Typing :: Typed
17
+ Requires-Dist: httpx2>=2.13.0
18
+ Requires-Dist: pycryptodome>=3.23.0
19
+ Requires-Python: >=3.13
20
+ Project-URL: Homepage, https://github.com/ngc2000/baark
21
+ Project-URL: Documentation, https://github.com/ngc2000/baark#readme
22
+ Project-URL: Source, https://github.com/ngc2000/baark
23
+ Project-URL: Issues, https://github.com/ngc2000/baark/issues
24
+ Project-URL: Releases, https://github.com/ngc2000/baark/releases
25
+ Description-Content-Type: text/markdown
26
+
27
+ # baark
28
+
29
+ A typed Python SDK for [Bark](https://bark.day.app/) push notifications. Matching
30
+ sync and async clients, connection pooling, rich notifications, and optional AES
31
+ encryption. Requires Python 3.13+; built on `httpx2` and PyCryptodome.
32
+
33
+ ## Install
34
+
35
+ From PyPI:
36
+
37
+ ```sh
38
+ uv add baark
39
+ ```
40
+
41
+ ## Quick start
42
+
43
+ Copy your device key from Bark. A URL ending in the key also works.
44
+
45
+ ```python
46
+ from baark import Baark
47
+
48
+ with Baark("YOUR_DEVICE_KEY", group="backups") as client:
49
+ result = client.send(
50
+ "The backup finished.",
51
+ title="Backup",
52
+ logo="https://example.com/backup.png",
53
+ url="https://example.com/backups/latest",
54
+ )
55
+ print(result.ok)
56
+ ```
57
+
58
+ Reuse the client for repeated sends; the context manager closes its connections.
59
+ `result.ok` confirms server acceptance, not delivery to the phone. For a single
60
+ send, use `baark.send("Hello", device_key="YOUR_DEVICE_KEY")`.
61
+
62
+ ### Async
63
+
64
+ ```python
65
+ import asyncio
66
+
67
+ from baark import AsyncBaark
68
+
69
+
70
+ async def main() -> None:
71
+ async with AsyncBaark("YOUR_DEVICE_KEY") as client:
72
+ await client.send("Job finished", title="Build")
73
+
74
+
75
+ asyncio.run(main())
76
+ ```
77
+
78
+ Both clients have the same options and operations; await async network calls.
79
+ The one-shot async helper is `baark.async_send()`.
80
+
81
+ ### Notification options
82
+
83
+ Pass options to `send()`, store them in a `Message`, or set client defaults.
84
+ Per-call options override message fields, which override client defaults.
85
+ Pass `None` in a call to clear an inherited option.
86
+
87
+ | Purpose | Options |
88
+ | --- | --- |
89
+ | Content | `body`, `title`, `subtitle`, `markdown` |
90
+ | Media | `icon` / `logo` (aliases), `image`, `sound` |
91
+ | Attention | `level`, `volume`, `badge`, `call` |
92
+ | Interaction | `url`, `action`, `copy`, `auto_copy` |
93
+ | History | `group`, `archive`, `ttl` |
94
+ | Lifecycle | `id`, `delete` |
95
+
96
+ Use Python booleans for flags. Media URLs must be reachable by the phone.
97
+ See [message types](https://github.com/ngc2000/baark/blob/main/src/baark/models.py) for all options and
98
+ [Bark's field reference](https://github.com/Finb/Bark/blob/master/docs/en-us/tutorial.md)
99
+ for device behavior.
100
+
101
+ ### Update and delete
102
+
103
+ ```python
104
+ with Baark("YOUR_DEVICE_KEY") as client:
105
+ client.send("Downloading…", id="download-42")
106
+ client.update("download-42", "Complete")
107
+ client.delete("download-42")
108
+ ```
109
+
110
+ Updates replace the complete message using the same ID. They require Bark 1.5.2+
111
+ and bark-server 2.2.5+. Deletion removes the notification and its history entry;
112
+ it requires Background App Refresh and may be delayed by iOS.
113
+
114
+ ## Encryption
115
+
116
+ **Use GCM for new configurations.** It encrypts the message and detects ciphertext
117
+ tampering. CBC and ECB are available for compatibility with existing Bark settings.
118
+
119
+ ```python
120
+ import os
121
+
122
+ from baark import Baark, Encryption
123
+
124
+ settings = Encryption(os.environ["BAARK_ENCRYPTION_KEY"]) # GCM by default
125
+ with Baark("YOUR_DEVICE_KEY", encryption=settings) as client:
126
+ client.send("Private content", title="Private title")
127
+ ```
128
+
129
+ Use the same key, algorithm, mode, and padding in Bark's **Push Encryption**
130
+ settings. Keys are 16, 24, or 32 printable ASCII characters, selecting AES-128,
131
+ AES-192, or AES-256. Key size and mode are separate choices: a longer key does
132
+ not add tamper detection to CBC or ECB.
133
+
134
+ | Mode | Protection | IV / nonce in baark | Bark padding | Choose it for |
135
+ | --- | --- | --- | --- | --- |
136
+ | **GCM** (default) | Encrypts and detects tampering | Fresh 12-character nonce per message | `noPadding` | New setups |
137
+ | **CBC** | Encrypts; no tamper detection | Fresh 16-character IV by default | `pkcs7` | Existing CBC setups |
138
+ | **ECB** | Encrypts but reveals repeated blocks; no tamper detection | None | `pkcs7` | Legacy compatibility only |
139
+
140
+ An IV or nonce is a public value used alongside the key. Leave `iv` unset so
141
+ baark generates it for each GCM or CBC message; Bark reads it from the request.
142
+ Fixed GCM nonces are rejected because reuse breaks its security. Fixed CBC IVs
143
+ are supported for compatibility, but reuse leaks repeated message prefixes.
144
+ See PyCryptodome's [GCM](https://pycryptodome.readthedocs.io/en/latest/src/cipher/modern.html#gcm-mode)
145
+ and [CBC/ECB](https://pycryptodome.readthedocs.io/en/latest/src/cipher/classic.html)
146
+ documentation for the underlying modes.
147
+
148
+ To create settings, run `settings = Encryption.generate()` once, save the key, and enter
149
+ `settings.key`, `settings.algorithm`, `settings.mode`, and `settings.padding` in
150
+ Bark. It defaults to AES-256-GCM. To import an encoded key, use
151
+ `Encryption.from_key(value, encoding="hex")` or `encoding="base64"`; enter the
152
+ decoded key in Bark. Hex and base64 are encodings, not encryption methods.
153
+
154
+ Encryption hides message fields, including titles and media URLs, from the Bark
155
+ server. Device routing, IVs, and `id`/`delete` controls remain visible. The key is
156
+ never sent. Without this option, HTTPS protects transport but the server can read
157
+ notification content.
158
+
159
+ ## Configuration and errors
160
+
161
+ Set `server_url=` for a custom server. `Baark.from_env()` and
162
+ `AsyncBaark.from_env()` read these optional settings from the process environment:
163
+
164
+ | Variable | Value |
165
+ | --- | --- |
166
+ | `BAARK_DEVICE_KEY` | Device key or URL ending in the key |
167
+ | `BAARK_SERVER_URL` | Custom server URL |
168
+ | `BAARK_ENCRYPTION_KEY` | Literal key matching Bark |
169
+ | `BAARK_ENCRYPTION_MODE` | `GCM` (default), `CBC`, or `ECB` |
170
+ | `BAARK_ENCRYPTION_IV` | Optional fixed CBC IV |
171
+
172
+ Ordinary constructors do not load these variables or config files. HTTP requests
173
+ default to a 10-second timeout, with redirects and retries disabled. Set
174
+ `timeout=`, `auth=`, or inject an `httpx2.Client` / `AsyncClient` with `http_client=`
175
+ for custom transport settings. Injected clients remain caller-owned; configure
176
+ authentication on them.
177
+ HTTP query defaults such as authentication tokens are preserved; notification and
178
+ routing fields come from baark's arguments and messages.
179
+
180
+ Send to multiple devices with `client.send("Hello", device_keys=["KEY_A", "KEY_B"])`.
181
+ Encrypted batches require all recipients to share encryption settings.
182
+ Partial failures raise `BatchError`; inspect `error.response.deliveries` for each
183
+ result. All SDK errors inherit from `BaarkError`. `APIError` exposes
184
+ `status_code`, `code`, and `apns_reason`; raw `server_message` is available
185
+ explicitly and may contain sensitive data.
186
+
187
+ For alternate endpoints, server utilities, registration, and URL building, see
188
+ the [client API](https://github.com/ngc2000/baark/blob/main/src/baark/client.py). Also see
189
+ [response and message types](https://github.com/ngc2000/baark/blob/main/src/baark/models.py),
190
+ [errors](https://github.com/ngc2000/baark/blob/main/src/baark/errors.py), and
191
+ [encryption helpers](https://github.com/ngc2000/baark/blob/main/src/baark/crypto.py).
192
+
193
+ ## Alternatives
194
+
195
+ | Project | Useful when you want | Documented encryption support |
196
+ | --- | --- | --- |
197
+ | **baark** | Reusable, typed sync and async Bark clients in Python | GCM, CBC, ECB |
198
+ | [iskoldtbark](https://pypi.org/project/iskoldtbark/) | A Bark CLI with saved devices, recipient groups, and per-device keys | AES-256-GCM; AES-128/192/256-CBC |
199
+ | [bark-python](https://pypi.org/project/bark-python/) | A small client with replaceable encryption strategies | Built-in CBC and ECB; custom strategies |
200
+ | [Apprise](https://appriseit.com/) | One library or CLI for Bark and other notification services | [Bark AES-GCM](https://appriseit.com/services/bark/#encryption) |
201
+
202
+ For simple scripts, [Bark's HTTP API](https://github.com/Finb/Bark/blob/master/docs/en-us/tutorial.md)
203
+ also works directly with curl or your existing HTTP client.
204
+
205
+ ## Development
206
+
207
+ Install [uv](https://docs.astral.sh/uv/), [just](https://just.systems/), and
208
+ [actionlint](https://github.com/rhysd/actionlint), then run `uv sync --locked`.
209
+ Windows development also needs Bash, as provided by Git for Windows.
210
+
211
+ ### Test with your phone
212
+
213
+ Copy `.env.example` with `cp -n .env.example .env`, then edit `.env` with your
214
+ device key and optional encryption settings. Leave unused settings commented out.
215
+
216
+ ```sh
217
+ uv run python scripts/e2e.py --list-cases
218
+ uv run --env-file .env python scripts/e2e.py --client sync --case logo image markdown
219
+ ```
220
+
221
+ These selected cases send three real notifications. Check the phone, then repeat
222
+ with the same client and cases plus `--action update` or `--action delete`.
223
+ `--case all` covers nine cases; the default client selection, `both`, doubles
224
+ the count. Use `--help` for media URL overrides and other options.
225
+
226
+ ### Offline checks
227
+
228
+ ```sh
229
+ just check
230
+ just build
231
+ just test-wheel
232
+ ```
233
+
234
+ The test suite uses mock HTTP transports and encryption vectors. Coverage checks
235
+ enforce the threshold in `pyproject.toml`. CI tests the built wheel on Python 3.13
236
+ and 3.14 across Linux, macOS, and Windows.
237
+
238
+ ## License
239
+
240
+ [MIT](https://github.com/ngc2000/baark/blob/main/LICENSE).
baark-0.1.0/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # baark
2
+
3
+ A typed Python SDK for [Bark](https://bark.day.app/) push notifications. Matching
4
+ sync and async clients, connection pooling, rich notifications, and optional AES
5
+ encryption. Requires Python 3.13+; built on `httpx2` and PyCryptodome.
6
+
7
+ ## Install
8
+
9
+ From PyPI:
10
+
11
+ ```sh
12
+ uv add baark
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ Copy your device key from Bark. A URL ending in the key also works.
18
+
19
+ ```python
20
+ from baark import Baark
21
+
22
+ with Baark("YOUR_DEVICE_KEY", group="backups") as client:
23
+ result = client.send(
24
+ "The backup finished.",
25
+ title="Backup",
26
+ logo="https://example.com/backup.png",
27
+ url="https://example.com/backups/latest",
28
+ )
29
+ print(result.ok)
30
+ ```
31
+
32
+ Reuse the client for repeated sends; the context manager closes its connections.
33
+ `result.ok` confirms server acceptance, not delivery to the phone. For a single
34
+ send, use `baark.send("Hello", device_key="YOUR_DEVICE_KEY")`.
35
+
36
+ ### Async
37
+
38
+ ```python
39
+ import asyncio
40
+
41
+ from baark import AsyncBaark
42
+
43
+
44
+ async def main() -> None:
45
+ async with AsyncBaark("YOUR_DEVICE_KEY") as client:
46
+ await client.send("Job finished", title="Build")
47
+
48
+
49
+ asyncio.run(main())
50
+ ```
51
+
52
+ Both clients have the same options and operations; await async network calls.
53
+ The one-shot async helper is `baark.async_send()`.
54
+
55
+ ### Notification options
56
+
57
+ Pass options to `send()`, store them in a `Message`, or set client defaults.
58
+ Per-call options override message fields, which override client defaults.
59
+ Pass `None` in a call to clear an inherited option.
60
+
61
+ | Purpose | Options |
62
+ | --- | --- |
63
+ | Content | `body`, `title`, `subtitle`, `markdown` |
64
+ | Media | `icon` / `logo` (aliases), `image`, `sound` |
65
+ | Attention | `level`, `volume`, `badge`, `call` |
66
+ | Interaction | `url`, `action`, `copy`, `auto_copy` |
67
+ | History | `group`, `archive`, `ttl` |
68
+ | Lifecycle | `id`, `delete` |
69
+
70
+ Use Python booleans for flags. Media URLs must be reachable by the phone.
71
+ See [message types](https://github.com/ngc2000/baark/blob/main/src/baark/models.py) for all options and
72
+ [Bark's field reference](https://github.com/Finb/Bark/blob/master/docs/en-us/tutorial.md)
73
+ for device behavior.
74
+
75
+ ### Update and delete
76
+
77
+ ```python
78
+ with Baark("YOUR_DEVICE_KEY") as client:
79
+ client.send("Downloading…", id="download-42")
80
+ client.update("download-42", "Complete")
81
+ client.delete("download-42")
82
+ ```
83
+
84
+ Updates replace the complete message using the same ID. They require Bark 1.5.2+
85
+ and bark-server 2.2.5+. Deletion removes the notification and its history entry;
86
+ it requires Background App Refresh and may be delayed by iOS.
87
+
88
+ ## Encryption
89
+
90
+ **Use GCM for new configurations.** It encrypts the message and detects ciphertext
91
+ tampering. CBC and ECB are available for compatibility with existing Bark settings.
92
+
93
+ ```python
94
+ import os
95
+
96
+ from baark import Baark, Encryption
97
+
98
+ settings = Encryption(os.environ["BAARK_ENCRYPTION_KEY"]) # GCM by default
99
+ with Baark("YOUR_DEVICE_KEY", encryption=settings) as client:
100
+ client.send("Private content", title="Private title")
101
+ ```
102
+
103
+ Use the same key, algorithm, mode, and padding in Bark's **Push Encryption**
104
+ settings. Keys are 16, 24, or 32 printable ASCII characters, selecting AES-128,
105
+ AES-192, or AES-256. Key size and mode are separate choices: a longer key does
106
+ not add tamper detection to CBC or ECB.
107
+
108
+ | Mode | Protection | IV / nonce in baark | Bark padding | Choose it for |
109
+ | --- | --- | --- | --- | --- |
110
+ | **GCM** (default) | Encrypts and detects tampering | Fresh 12-character nonce per message | `noPadding` | New setups |
111
+ | **CBC** | Encrypts; no tamper detection | Fresh 16-character IV by default | `pkcs7` | Existing CBC setups |
112
+ | **ECB** | Encrypts but reveals repeated blocks; no tamper detection | None | `pkcs7` | Legacy compatibility only |
113
+
114
+ An IV or nonce is a public value used alongside the key. Leave `iv` unset so
115
+ baark generates it for each GCM or CBC message; Bark reads it from the request.
116
+ Fixed GCM nonces are rejected because reuse breaks its security. Fixed CBC IVs
117
+ are supported for compatibility, but reuse leaks repeated message prefixes.
118
+ See PyCryptodome's [GCM](https://pycryptodome.readthedocs.io/en/latest/src/cipher/modern.html#gcm-mode)
119
+ and [CBC/ECB](https://pycryptodome.readthedocs.io/en/latest/src/cipher/classic.html)
120
+ documentation for the underlying modes.
121
+
122
+ To create settings, run `settings = Encryption.generate()` once, save the key, and enter
123
+ `settings.key`, `settings.algorithm`, `settings.mode`, and `settings.padding` in
124
+ Bark. It defaults to AES-256-GCM. To import an encoded key, use
125
+ `Encryption.from_key(value, encoding="hex")` or `encoding="base64"`; enter the
126
+ decoded key in Bark. Hex and base64 are encodings, not encryption methods.
127
+
128
+ Encryption hides message fields, including titles and media URLs, from the Bark
129
+ server. Device routing, IVs, and `id`/`delete` controls remain visible. The key is
130
+ never sent. Without this option, HTTPS protects transport but the server can read
131
+ notification content.
132
+
133
+ ## Configuration and errors
134
+
135
+ Set `server_url=` for a custom server. `Baark.from_env()` and
136
+ `AsyncBaark.from_env()` read these optional settings from the process environment:
137
+
138
+ | Variable | Value |
139
+ | --- | --- |
140
+ | `BAARK_DEVICE_KEY` | Device key or URL ending in the key |
141
+ | `BAARK_SERVER_URL` | Custom server URL |
142
+ | `BAARK_ENCRYPTION_KEY` | Literal key matching Bark |
143
+ | `BAARK_ENCRYPTION_MODE` | `GCM` (default), `CBC`, or `ECB` |
144
+ | `BAARK_ENCRYPTION_IV` | Optional fixed CBC IV |
145
+
146
+ Ordinary constructors do not load these variables or config files. HTTP requests
147
+ default to a 10-second timeout, with redirects and retries disabled. Set
148
+ `timeout=`, `auth=`, or inject an `httpx2.Client` / `AsyncClient` with `http_client=`
149
+ for custom transport settings. Injected clients remain caller-owned; configure
150
+ authentication on them.
151
+ HTTP query defaults such as authentication tokens are preserved; notification and
152
+ routing fields come from baark's arguments and messages.
153
+
154
+ Send to multiple devices with `client.send("Hello", device_keys=["KEY_A", "KEY_B"])`.
155
+ Encrypted batches require all recipients to share encryption settings.
156
+ Partial failures raise `BatchError`; inspect `error.response.deliveries` for each
157
+ result. All SDK errors inherit from `BaarkError`. `APIError` exposes
158
+ `status_code`, `code`, and `apns_reason`; raw `server_message` is available
159
+ explicitly and may contain sensitive data.
160
+
161
+ For alternate endpoints, server utilities, registration, and URL building, see
162
+ the [client API](https://github.com/ngc2000/baark/blob/main/src/baark/client.py). Also see
163
+ [response and message types](https://github.com/ngc2000/baark/blob/main/src/baark/models.py),
164
+ [errors](https://github.com/ngc2000/baark/blob/main/src/baark/errors.py), and
165
+ [encryption helpers](https://github.com/ngc2000/baark/blob/main/src/baark/crypto.py).
166
+
167
+ ## Alternatives
168
+
169
+ | Project | Useful when you want | Documented encryption support |
170
+ | --- | --- | --- |
171
+ | **baark** | Reusable, typed sync and async Bark clients in Python | GCM, CBC, ECB |
172
+ | [iskoldtbark](https://pypi.org/project/iskoldtbark/) | A Bark CLI with saved devices, recipient groups, and per-device keys | AES-256-GCM; AES-128/192/256-CBC |
173
+ | [bark-python](https://pypi.org/project/bark-python/) | A small client with replaceable encryption strategies | Built-in CBC and ECB; custom strategies |
174
+ | [Apprise](https://appriseit.com/) | One library or CLI for Bark and other notification services | [Bark AES-GCM](https://appriseit.com/services/bark/#encryption) |
175
+
176
+ For simple scripts, [Bark's HTTP API](https://github.com/Finb/Bark/blob/master/docs/en-us/tutorial.md)
177
+ also works directly with curl or your existing HTTP client.
178
+
179
+ ## Development
180
+
181
+ Install [uv](https://docs.astral.sh/uv/), [just](https://just.systems/), and
182
+ [actionlint](https://github.com/rhysd/actionlint), then run `uv sync --locked`.
183
+ Windows development also needs Bash, as provided by Git for Windows.
184
+
185
+ ### Test with your phone
186
+
187
+ Copy `.env.example` with `cp -n .env.example .env`, then edit `.env` with your
188
+ device key and optional encryption settings. Leave unused settings commented out.
189
+
190
+ ```sh
191
+ uv run python scripts/e2e.py --list-cases
192
+ uv run --env-file .env python scripts/e2e.py --client sync --case logo image markdown
193
+ ```
194
+
195
+ These selected cases send three real notifications. Check the phone, then repeat
196
+ with the same client and cases plus `--action update` or `--action delete`.
197
+ `--case all` covers nine cases; the default client selection, `both`, doubles
198
+ the count. Use `--help` for media URL overrides and other options.
199
+
200
+ ### Offline checks
201
+
202
+ ```sh
203
+ just check
204
+ just build
205
+ just test-wheel
206
+ ```
207
+
208
+ The test suite uses mock HTTP transports and encryption vectors. Coverage checks
209
+ enforce the threshold in `pyproject.toml`. CI tests the built wheel on Python 3.13
210
+ and 3.14 across Linux, macOS, and Windows.
211
+
212
+ ## License
213
+
214
+ [MIT](https://github.com/ngc2000/baark/blob/main/LICENSE).
baark-0.1.0/justfile ADDED
@@ -0,0 +1,40 @@
1
+ set default-list := true
2
+ set dotenv-load := false
3
+ set shell := ["bash", "-euo", "pipefail", "-c"]
4
+ set windows-shell := ["bash", "-euo", "pipefail", "-c"]
5
+
6
+ # Format Python source.
7
+ format:
8
+ uv run --locked python -m ruff format .
9
+
10
+ # Check the lockfile, lint, formatting, and types without changing files.
11
+ lint:
12
+ uv lock --check
13
+ uv run --locked python -m ruff check .
14
+ uv run --locked python -m ruff format --check .
15
+ uv run --locked python -m ty check
16
+
17
+ # Validate GitHub Actions (requires actionlint).
18
+ lint-workflows:
19
+ actionlint .github/workflows/*.yml
20
+
21
+ # Run offline tests and enforce coverage.
22
+ test:
23
+ uv run --locked python -m coverage run
24
+ uv run --locked python -m coverage report
25
+
26
+ # Run all source and workflow checks.
27
+ check: lint lint-workflows test
28
+
29
+ # Build fresh distributions and check their metadata and contents.
30
+ build:
31
+ uv build --clear --no-sources
32
+ uv run --locked python -m twine check --strict dist/*
33
+ uv run --locked python scripts/check_dist.py
34
+
35
+ # Install the built wheel, then run the offline suite against that installation.
36
+ test-wheel:
37
+ uv sync --locked --no-install-project
38
+ uv pip install --no-deps --reinstall dist/*.whl
39
+ uv run --no-sync python -I scripts/check_wheel.py
40
+ UV_NO_SYNC=true just test