Add Bark transport for free-sideload notification delivery - #9
Conversation
Free-Apple-ID sideloads can't obtain the aps-environment entitlement, so
APNs registration always fails and notifications were paid-cert-only.
This adds a per-device delivery transport so those devices can receive
notifications via Bark (App Store app id1403753865) instead:
- devices gain transport ('apns'|'bark') + transport_endpoint columns
(migration 000014, mirrored in docs/schema.sql and the docker migrate
patch mounts).
- New internal/push.Sender routes each send per-device: APNs path is
exact behavioral parity with the old inline apns2 calls; the Bark path
POSTs the payload as Bark JSON (title/subtitle/body/badge/group/icon)
with an apollo:// click URL (thread deep link, or apollo://reborn/inbox
for private messages). Bark failures never set ShouldUnregister, so a
down bark-server or bad key can't destroy the registration/watcher
graph; only permanent APNs rejections still delete device rows.
- All four worker send sites plus both API test endpoints go through the
sender, so /v1/device/{token}/test/* exercises the full Bark path.
- upsertDeviceHandler honors X-Apollo-Transport /
X-Apollo-Transport-Endpoint headers over the body (Apollo posts
/v1/device as an upload task, so the tweak can only reliably tag the
request via headers) and now validates the device (422 on bad input).
- docker-compose gains an opt-in self-hosted bark-server service behind
the "bark" profile (make docker-up-bark), with docs in README and
GETTING_STARTED.
Bark pushes with no post thumbnail showed the Bark app icon. The sender now falls back to Apollo's stock app icon, hosted in the Apollo-Reborn repo (assets/bark-icons/default.png); BARK_DEFAULT_ICON overrides the URL. Thumbnail-bearing pushes keep the post image, and devices whose push URL pins ?icon= (the tweak does this when the user selected an alternate app icon in Apollo) override both — bark-server gives query parameters priority over the JSON body.
Bark pushes played the default iOS tone: the payload's sound
(traloop.wav) references a file in Apollo's bundle, and Apollo's
notification service extension — which swaps in the user's in-app sound
pick — never runs for deliveries that land in the Bark app.
The Bark translation now forwards the payload's sound name minus its
extension ("traloop"), matching the .caf conversions of Apollo's sounds
hosted in Apollo-Reborn's assets/bark-sounds (bark-server appends
".caf" to extensionless values). The user imports the .caf for their
sound into the Bark app once; sounds that aren't imported fall back to
the default alert tone, so nothing breaks without the import. The
tweak pins the in-app sound pick via ?sound= on the push URL, which
overrides this body-level value the same way ?icon= does.
golangci-lint (testpackage, copyloopvar, usetesting) has been failing the Unit Tests workflow since bark_test.go landed: internal tests belong in a *_internal_test.go file, the Go 1.22 loop-var copy is dead, and tests should use t.Context().
A Bark-only self-host (docker compose --profile bark) had no way to start: every worker and the api called LoadAPNS unconditionally and crash-looped without a .p8, spamming rmq heartbeat errors from the teardown of the already-opened queue client. Reported by Gliglue on the announcement thread. LoadAPNS is now tri-modal on the four APPLE_* vars: all set works as before, all empty disables APNs (nil token, logged as Bark-only mode), and a partial set fails fast naming the missing vars. It also runs before any queue client exists, so config failures no longer produce heartbeat noise. In Bark-only mode the sender skips building apns2 clients and APNs sends fail with a plain error — deliberately not ShouldUnregister, which would have deleted every legacy APNs device row. APNs device registrations and Live Activity registrations get a 422 explaining the mode, and the live-activities worker drops (and deletes) any leftover rows instead of pushing.
Plain 'docker compose up' reuses the previously built image, so after a git pull people silently keep running old code — exactly how the first Bark-only tester ended up debugging a fix they didn't have. Bake --build into the docker-up/docker-up-bark targets and every documented compose invocation; with layer caching a no-change rebuild costs seconds. The prebuilt-registry-image flow (compose pull, no --build) stays documented as the explicit alternative, with its caveats spelled out.
Tapping a native notification for a reply or mention lands on the specific comment; Bark taps only opened the post. The payload already carries comment_id, so emit Apollo's own share-link form — /r/<sub>/comments/<post>/_/<comment>/?context=1 — which round-trips its link parser (regex verified against the binary: the comment id is captured as (\w+) after an optional (?:/\w+)? slug, so the placeholder slug must be _, not - — a /-/ link silently opens the post unanchored). context=1 shows the parent above the comment, matching the native tap.
|
Testing this w/ Bark works great, one issue regarding thumbnail implementation, Reddit's API returns |
Reddit fills the thumbnail field with "self", "default", "nsfw", "spoiler", or "image" when a post has no real thumbnail. The Bark translation copied any non-empty value into the icon field, which both sent a bogus icon URL and suppressed the default-icon fallback, so text posts showed Bark's icon instead of Apollo's. Only accept http(s) URLs as icons. Reported in PR #9 by @Write.
|
Good catch, thanks for testing! You're right — Reddit uses sentinel values ( Fixed in eb4c155: the Bark translation layer now only accepts |
The push URL is registrant-supplied, so chasing a 3xx would let a registration bounce the notification POST at an address of the redirector's choosing — an SSRF amplifier on open-registration deployments. bark-server never redirects, so surface the 3xx as a failed send instead, and name the SSRF trade-off in the README next to the bearer-capability and plaintext caveats.
When X-Apollo-Transport arrived without X-Apollo-Transport-Endpoint, the empty header overwrote a transport_endpoint the body did manage to carry, turning a registrable request into a 422. Headers still win whenever they're present; the body value now survives only when the endpoint header is absent.
- GETTING_STARTED.md is now APNs-only with a path chooser up top, dropping the interleaved "Bark-only mode? skip this" asides and the outdated "no way around the $99/year" note - New GETTING_STARTED_BARK.md: self-contained free path from Docker install to a verified Bark notification; links to the main guide for the transport-agnostic exposure and long-term-ops sections - New ORACLE_CLOUD.md (first draft): Always Free tier walkthrough covering A1 capacity/region gotchas, idle reclamation, and the VCN + host-iptables double firewall; all stack images verified arm64 - README points at both guides plus the Oracle option
Summary
Adds a per-device Bark transport so notifications reach free-Apple-ID sideloads that can't register for APNs. Pairs with Apollo-Reborn/Apollo-Reborn#578, which registers devices with
transport: bark+ a Bark push URL.Changes
000014addsdevices.transport(apns/bark, defaultapns) +transport_endpoint; also wired intodocs/schema.sqland the dockermigrate.shidempotent patch path. Repositories scan/write both columns everywhere devices are fetched, including the watcher-joined SELECTs.internal/pushpackage: aSenderthat routes per-device — APNs path is exact parity with the previous inline code (prod/sandbox, permanent-rejection unregistration); the Bark path POSTs a JSON push to the device's endpoint with title/body/badge/group and anapollo://deep link opened on tap. Bark failures never unregister a device — a down relay or bad key must not destroy the watcher graph./v1/device/<token>/test/*routes exercise the full Bark path.X-Apollo-Transport(-Endpoint)headers (which win over the body) and validates bark devices (endpoint required, http/https).BARK_DEFAULT_ICONenv, sane default).traloop.wav) is forwarded extensionless so Bark plays the matching imported.caf; per-user picks ride as?sound=on the stored push URL (query beats body on bark-server), needing no schema here.bark-serverbehind thebarkprofile (make docker-up-bark).Testing
go test ./...green; new unit tests cover payload→Bark translation for every notification category, deep-link escaping, default-icon fallback, and sound forwarding.