docs: the server is on the sprite and internet-exposed
The long-standing open question "where will the server actually live" is answered for now: it stays on this sprite, bound to 0.0.0.0:8090 and published over HTTPS by the sprite proxy. Several docs asserted the opposite — HANDOFF said "127.0.0.1:8090, deliberately NOT internet-exposed (no --http-port, so the sprite proxy can't reach it)", which is flatly wrong today. The consequence is the part worth writing down: PocketBase's API rules are now the only thing between this library and the internet. There is no NAT, no VPN, no reverse proxy. So the anonymous-access curls stop being a formality, and they have to run against the PUBLIC hostname — localhost cannot tell you what the world can reach. Re-verified that way: books/shelves/bookcases LIST all 403, self-registration 403, health 200. One gap found while re-verifying, recorded but NOT fixed: users LIST answers 200 with an empty array instead of 403. Nothing is disclosed — two real accounts exist and the listRule filters both out — but it is the same wrong-signal quirk pb_hooks/main.pb.js exists to close, and that hook never listed the users collection. SPEC's offline-first rationale is amended rather than its rule: the reason is no longer residential NAT but a sprite that suspends when idle and wakes on request. The rule is unchanged and does not depend on which. server/deploy/ still documents systemd/Docker/Tailscale on home hardware; it now says up front that this is the intended end state, not what is running. Also corrected, since it was adjacent and plainly false: README still claimed the app had never run on a physical device. It has, since 2026-09-09. What is true is that no *automated* test runs on a device — there is no emulator on this box. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPpdG8VnRfS3KkisR3HUAE
This commit is contained in:
1 parent
26f76bf0f7
commit
1385ad286f
5 files changed
+149
-26
No files matched your search
+59
-7
@@ -64,11 +64,60 @@ change it, write a NEW file and use that for the next wave.
|
||||
- Android SDK: `~/toolchain/android-sdk` (platforms;android-37.0, build-tools;37.0.0, platform-tools)
|
||||
- **No KVM, no emulator.** Verify only via `./gradlew assembleDebug`, JVM unit tests, and
|
||||
Paparazzi PNG rendering. Never claim the app was "run".
|
||||
- PocketBase v0.40.2 service, **127.0.0.1:8090, deliberately NOT internet-exposed**
|
||||
(no `--http-port`, so the sprite proxy can't reach it). Restart:
|
||||
`sprite-env services restart pocketbase`. Logs: `/.sprite/logs/services/pocketbase.log`.
|
||||
- PocketBase v0.40.2 service, **bound to `0.0.0.0:8090` and INTERNET-EXPOSED** via the
|
||||
sprite proxy at **https://bookshelf-dev-b2jqx.sprites.app** (the service is registered
|
||||
with `--http-port 8090`, so the proxy routes to it). This is the live server the user's
|
||||
phone talks to. Restart: `sprite-env services restart pocketbase`.
|
||||
Logs: `/.sprite/logs/services/pocketbase.log`. See "Where the server lives" below.
|
||||
- Superuser creds: `server/.dev-credentials` (gitignored).
|
||||
|
||||
## Where the server lives — ANSWERED 2026-09-12: on this sprite, publicly
|
||||
This was an open question through waves 1-7 and it now has an answer: **for now the
|
||||
server stays here on the sprite and is reachable from the open internet.** The user's
|
||||
phone talks to `https://bookshelf-dev-b2jqx.sprites.app`. Earlier notes in this file
|
||||
and in `server/deploy/` said the opposite (localhost-only, unreachable from outside);
|
||||
they were true when written and are now corrected in place. `server/deploy/` still
|
||||
documents systemd/Docker/Tailscale for an eventual move to home hardware — that is a
|
||||
future option, not what is running.
|
||||
|
||||
Consequences that were not true when the app was designed:
|
||||
|
||||
1. **PocketBase's own API rules are now the ONLY thing between this library and the
|
||||
internet.** There is no NAT, no Tailscale, no reverse proxy in front of it. The
|
||||
security curls in "Verification standard" below stopped being a formality the day
|
||||
this changed — run them against the PUBLIC hostname, not `127.0.0.1`, because
|
||||
localhost cannot tell you what the world can reach.
|
||||
2. **The sprite auto-suspends when idle** and wakes on an incoming HTTP request
|
||||
(that is what `--http-port` buys). So the server is still "often unreachable" in
|
||||
the sense SPEC's offline-first rule cares about — just for a different reason than
|
||||
residential NAT, and with a cold-start delay on the first request after a pause
|
||||
rather than a hard failure. The size of that delay has **not** been measured; if a
|
||||
sync or a first login ever looks pathologically slow, measure it before assuming a
|
||||
bug in the app.
|
||||
3. The URL is a sprite-scoped hostname. If this sprite is ever renamed or rebuilt the
|
||||
URL changes, and every phone has to be re-pointed at it on the setup screen. That
|
||||
is an argument for moving to Tailscale + real hardware eventually, not a reason to
|
||||
hardcode anything.
|
||||
|
||||
### Posture re-verified against the PUBLIC hostname, 2026-09-12
|
||||
| Check (anonymous, over the internet) | Result |
|
||||
|---|---|
|
||||
| `GET /api/collections/books/records` | **403** `{"message":"Authentication required."}` |
|
||||
| `GET /api/collections/shelves/records` | **403** |
|
||||
| `GET /api/collections/bookcases/records` | **403** |
|
||||
| `POST /api/collections/users/records` (self-registration) | **403** |
|
||||
| `GET /api/health` | 200 (intended — it is a health check and leaks nothing) |
|
||||
| `GET /api/collections/users/records` | **200 `{"items":[]}`** — see below |
|
||||
|
||||
**Known gap, not a leak:** `users` LIST answers **200 with an empty array** instead of
|
||||
403. No data escapes — there are two real accounts in the DB and an anonymous caller
|
||||
sees neither, because `listRule` filters the rows out — but the status code is wrong
|
||||
and it is the exact PocketBase quirk `pb_hooks/main.pb.js` exists to paper over. That
|
||||
hook lists only `bookcases`, `shelves`, `books`; `users` was never added to it. One
|
||||
word of a fix, and worth doing now that the collection is world-reachable: some
|
||||
clients read "200 with []" as an allowed request. **Not yet done — do not record it as
|
||||
done until the curl above returns 403.**
|
||||
|
||||
## STATE: what is DONE
|
||||
### Wave 1A — server: COMPLETE and verified by the orchestrator (not just self-reported)
|
||||
`server/` contains `setup-schema.sh` (idempotent), `create-user.sh`, `pb_hooks/main.pb.js`,
|
||||
@@ -158,7 +207,8 @@ Workers self-report optimistically. Before accepting any wave:
|
||||
the user, not something to paper over — the user explicitly cares how this looks.
|
||||
|
||||
## Open questions for the user (not yet asked — deferred, not forgotten)
|
||||
- Where the server will actually live (home box vs a sprite) — only affects the deploy README.
|
||||
- ~~Where the server will actually live (home box vs a sprite)~~ — **ANSWERED 2026-09-12:
|
||||
it stays on this sprite, internet-exposed. See "Where the server lives" above.**
|
||||
- Their two account emails, for `create-user.sh`. Not needed until the app can log in.
|
||||
|
||||
## HAZARD #5 — THE SPRITE AUTO-SUSPENDS; detached workers do NOT keep it awake
|
||||
@@ -283,8 +333,9 @@ library, scaffold — each light + dark).
|
||||
3. **R8 is off.** Acceptable per F3's prompt, but the release APK is 41.8MB.
|
||||
Turning it on requires proving Room/Retrofit/kotlinx-serialization/ML Kit
|
||||
survive minification.
|
||||
4. Still-open user questions, unchanged: where the server will actually live, and
|
||||
the two account emails for `create-user.sh`.
|
||||
4. Still-open user questions: the two account emails for `create-user.sh`.
|
||||
(Where the server will live was ANSWERED on 2026-09-12 — it stays on this
|
||||
sprite, internet-exposed. See "Where the server lives" near the top.)
|
||||
|
||||
## First on-device test — 2026-09-09
|
||||
The user installed the signed APK on a real phone. It runs. This closes the
|
||||
@@ -372,7 +423,8 @@ that.
|
||||
**Still open, unchanged:** the free Google Books API key (keyless returns 429;
|
||||
worth doing on its own merits but no longer the leading theory), R8 still off so
|
||||
the release APK is 41.8MB and too large to send over the file channel (30MB cap),
|
||||
where the server will live, and the two account emails for `create-user.sh`.
|
||||
and the two account emails for `create-user.sh`. (Where the server will live
|
||||
was ANSWERED on 2026-09-12: it stays on this sprite, internet-exposed.)
|
||||
|
||||
## Wave 5 — G-diagnostics: COMPLETE, verified by the orchestrator 2026-09-09
|
||||
Commit `93f972b`. The app now distinguishes barcode-didn't-decode from
|
||||
|
||||
+8
-4
@@ -4,10 +4,14 @@ Two-person shared home library. Android app + self-hosted PocketBase.
|
||||
ALL workers must follow this exactly. Do not invent alternative names.
|
||||
|
||||
## Non-negotiables
|
||||
- Offline-first. Home server is often unreachable (residential NAT). Every read
|
||||
comes from Room. Every write lands in Room first, syncs later. No screen may
|
||||
block on network.
|
||||
- Private. No public registration. Auth required for all data access.
|
||||
- Offline-first. The server is often slow or unreachable. Every read comes from
|
||||
Room. Every write lands in Room first, syncs later. No screen may block on
|
||||
network. (The original reason was residential NAT; the server currently runs
|
||||
on a sprite that suspends when idle and wakes on request, so the same rule
|
||||
holds for a different reason. The requirement does not depend on which.)
|
||||
- Private. No public registration. Auth required for all data access. As of
|
||||
2026-09-12 the server is INTERNET-EXPOSED, so these rules are the only thing
|
||||
protecting the library — not defence-in-depth behind a home NAT.
|
||||
- Server URL is NOT hardcoded; user enters it on first run.
|
||||
|
||||
## Repo layout
|
||||
|
||||
Reference in new issue
Block a user