Deployment¶
The Loco Linux images workflow builds, checks and publishes the Rust image on
every push to main as ghcr.io/damacus/med-tracker-loco:sha-<full commit>.
Only the current main revision can update beta; reruns of older commits retain
their own image without changing beta. Its run summary records the source commit and immutable digest
to deploy. Manual publication from main remains available. The existing Rails
publisher was removed with the Rails archive.
Root Loco is the fresh Rust application; use the fresh Rust installation notes
for its image, configuration, new database and fresh-account requirements. Rust
portable import/export is supported within its documented limits. These notes do
not define a live cutover or require a full Rails export.
The Rails Compose and operator commands below describe the archived application. They are not the Rust rollout procedure.
Loco authentication secrets¶
Loco uses Better Auth with a persistent 64-byte, Base64-encoded
MEDTRACKER_SESSION_KEY, provisioned through the deployment secret store.
Development uses the committed synthetic browser-session key when the environment
variable is absent. Do not use that development key in production.
Loco uses Better Auth’s native schema, store and authentication plugins. It starts with fresh authentication data: Rails passwords, authenticator enrolments, passkeys, sessions and recovery tokens are not imported. Schema setup creates the native tables directly, without transitional credential tables or auth-data conversion. Use a new empty database; setup does not reset or adopt a populated one. Provisioning or replacing any populated database requires separate approval.
Users enrol native credentials and use native MFA backup codes or email/password recovery. A backup code completes the second sign-in step and cannot sign in alone. Passkey-only accounts add a password before enabling the native TOTP plugin. New authenticator enrolments require verification before activation.
Loco authentication does not need Rails secrets. Keep retained Rails reference credentials and secrets separate from the Rust deployment. See the authentication guide for native account flows.
Production browser, CSRF and Better Auth cookies share the validated secure-cookie
policy. MEDTRACKER_COOKIE_SECURE=false is rejected in production; TLS termination
at a proxy does not make authentication cookies non-Secure. Set the canonical
public scheme/hostname through MEDTRACKER_PUBLIC_HOST and ensure the configured
Loco port matches the public origin used for generated links and relying-party
verification. Internal HTTP alone does not establish the public origin.
Loco passkeys¶
Configure the server's canonical public origin before registering passkeys. The
relying-party ID comes from that origin's hostname; verification requires the
configured scheme, hostname and port. Use a DNS hostname, or localhost for the
owned browser fixtures. Native browsers reject an IP literal as a relying-party
ID.
Better Auth’s native passkey plugin owns registration, verification and storage.
Users register new Loco passkeys. Historical Rails credentials remain separate
reference data. The browser
client uses standard PublicKeyCredential JSON conversion methods; browsers
without those methods cannot complete this flow.
Fresh Rust release scope¶
ADR 0016 supersedes the earlier populated-schema adoption, credential migration and saved-state Rails rollback design. Start with a new empty PostgreSQL 18 database and fresh Rust credentials. Historical passkeys are not imported, regardless of algorithm. Do not reset existing databases or assume Rails can consume Rust-created data.
Use the fresh Rust installation notes for the image, fresh database, new accounts and configuration requirements. The documented portable-data contract covers supported imports and exports; it does not promise complete Rails-export restoration. There is no planned live cutover or mandatory full Rails export. Deployment remains a separate owner action.
The owner has accepted the delivered Slice 7 worker/notification scope and waived extra process stop/restart and SIGTERM rehearsals. Native/offline work, new export products, canary jobs and optional integrations retain their explicit deferrals. Physical notification checks remain deferred until after deployment. These decisions do not claim inbox receipt, device display or unperformed checks.
Invitation email transactions¶
Invitation delivery uses Loco's standard MailerWorker and Email payload.
Loco's PostgreSQL enqueue API takes a separate pool; it cannot join the existing
SeaORM tenant transaction. The invitation adapter inserts that standard job into
the owner-provisioned pg_loco_queue within the same transaction as the invitation,
token rotation and audit changes. Loco owns job execution and retries.
Before accepting this delivery, prove that an enqueue failure rolls back the invitation changes and that the registered worker consumes a committed job and delivers its email. Browser journeys use an isolated SMTP inbox to verify delivery and the actual acceptance link. A queue row alone is not delivery evidence.
Registration policy database access¶
The migration adds a SELECT policy for active-owner memberships to the existing
database owner role. Its fixed-search-path function
public.registration_has_active_owner() returns only a boolean to the application.
The application retains tenant row filtering, cannot assume the owner role and
receives no new table or schema permissions. Public function execution is revoked.
The owner role remains subject to forced row-level security; the additional policy
allows this bounded lookup. No new database role or operator bootstrap is needed.
Preserve the retained registration setting: INVITE_ONLY takes precedence;
otherwise initialise a missing stored setting from active-owner existence.
A stored false setting stays open after the first household is created. Neither
these migration files nor this documentation authorise a live database change.
Compose profiles¶
dev: development stacktest: test stackprod: local validation of the production image
Development deployment¶
Use Taskfile wrappers:
task rails:dev:portless
task rails:dev:seed
Stop or inspect:
task rails:dev:stop
task rails:dev:logs
task rails:dev:ps
Test deployment¶
Start/stop test services when needed:
task rails:test:up
task rails:test:stop
task rails:test:logs
Run full tests in the test environment:
task rails:test
Local production-image validation¶
The prod profile builds the production image and migrates its local PostgreSQL
database before starting the application. It is not a real production
deployment.
task rails:prod:build
task rails:prod:up
task rails:prod:ps
task rails:prod:up runs the migrate-prod service before starting the web service.
Use the Task wrappers to inspect and stop the local stack:
task rails:prod:logs
task rails:prod:stop
For a real reachable deployment, follow the hosted private beta runbook or the Kubernetes runbooks linked below.
Environment and database notes¶
See the production environment reference for required settings, database roles, registration policy, and process sizing.
- All environments use PostgreSQL.
- PostgreSQL version target is
18. - Use Rails credentials and environment variables for secrets. Never commit them.
- Existing databases created before 0.5 need the pre-0.5 database upgrade bootstrap before running 0.5 migrations.
- Leave
DATABASE_ROLEunset for migrations in existing shared-login deployments. The web process uses the same database login and may setDATABASE_ROLE=med_tracker_appfor runtime row-level security. Owner-role switching is deferred until there is an explicit ownership-adoption and rollback design for existing databases.
External API credentials¶
See optional integration configuration for the complete service inventory and privacy boundaries.
NHS dm+d medicine search¶
The medicine search feature requires a system-to-system account from the NHS England Terminology Server. See NHS dm+d Integration for the full setup guide including how to request credentials.
| Variable | Required | Description |
|---|---|---|
NHS_DMD_CLIENT_ID |
Yes | OAuth2 client ID from NHS |
NHS_DMD_CLIENT_SECRET |
Yes | OAuth2 client secret from NHS |
If either variable is absent, the medicine search feature is disabled and does not make NHS API calls.
Bootstrap the first administrator¶
Kubernetes operators should use the dedicated runbook for complete seeding procedures:
Use the runbook for both Kubernetes Jobs. It includes the required runtime settings, household selector, supported invitation roles, verification, and cleanup steps.
Quick flow selection:
| Goal | Command | Notes |
|---|---|---|
| Create first administrator account | rails med_tracker:bootstrap_admin |
One-off account creation with ADMIN_* vars |
| Invite initial care-team users | rails db:seed |
Reads /app/db/seeds/users.yml, idempotent skips |
After the first admin exists, self-registration without invitations is blocked.
Rebuild environments¶
Development rebuild (destructive to dev volumes):
task rails:dev:rebuild
Test rebuild (destructive to test volumes):
task rails:test:rebuild