The same registry photos got: any number of labelled instances stored encrypted in
invoiceshelf_accounts, one selected, switchable from the nav. The token is write-only across
the sidecar boundary — the list has no field that could carry it back — and nothing reads
INVOICESHELF_URL/TOKEN/COMPANY_ID any more, so officer's own process.env no longer holds a
credential only the sidecar can use.
The company is pinned on the account row rather than resolved per request. InvoiceShelf's
`company` header does not error on a wrong or missing value; it silently returns another
company's books. So the choice is made once, at add time, and a token that can act for several
answers 409 with the list instead of guessing.
Both apps also take an email and password now, because neither service makes a key easy to get:
InvoiceShelf 2.4.2 ships no screen that issues tokens at all (POST /auth/login is the only way),
and Immich's is buried in account settings. The sidecar does the exchange — InvoiceShelf mints a
Sanctum token, Immich logs in, creates an all-permissions API key and closes the session again —
and stores only what comes back. The password is never persisted. Pasting a key still works.
Verified against the live instances: InvoiceShelf 2.4.2 and Immich 3.1.0, routes and DTOs read
from the running containers. The two sign-in paths are untested end to end — no second login to
try them with.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
owns the invoiceshelf contract: instance url, sanctum token and the company
header that scopes every request. the platform side is the usual thin
auth+forward proxy at /api/invoiceshelf and holds no credentials.
built against the live 2.4.2 instance rather than the 3.0.0-alpha.1 checkout
in _references — the route allow-list came from artisan route:list on the
running container. they differ: 2.4.2 has estimates/{id}/convert-to-invoice
but no invoices/{id}/convert-to-estimate.
three upstream quirks absorbed here:
- accept: application/json is mandatory, or an unauthenticated request 302s
to an html login instead of returning 401
- origin/referer must never be sent, or statefulapi() switches to session+csrf
and every request 419s. the proxy forwards neither.
- a wrong company header does not error, it silently returns another company's
data. the pinned company is explicit and logged.
document pdfs are repaired: 2.4.2 prefixes them with a literal serialised http
response (201 bytes) inside a body already typed application/pdf. we slice to
the %PDF- magic. the report routes don't have the bug.
resources are an allow-list. backups, disks, modules, update/*, installation/*,
mail config, settings writes and ownership transfer stay unreachable, and the
per-resource action list keeps `send` — which really emails the customer —
from being reachable by accident.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>