3 Commits
Author SHA1 Message Date
pastilhas 91763f4bf4 kiosk: builds record when the profile expires
ios provisioning profiles last a year and the app stops launching when one
expires - it does not degrade, it refuses to start. that date drives the
re-sign queue and the reminder, and per BUSINESS-MODEL.md the annual re-sign is
the product rather than an upsell.

free to add today, a migration after the first publish.
2026-08-18 02:23:31 +00:00
Claude Opus 5 5374d40553 rename: OAS becomes Kiosk, everywhere
officer kiosk is the product name; OAS was a placeholder that mimicked expo's
application services and is gone.

the rename is total because nothing has shipped: appName, permission key, all
four table prefixes, every index and check constraint name, the exported drizzle
symbols, and the secret-store purpose. no rows exist anywhere, so there is no
migration and no compatibility window - which is exactly why doing it now cost
nothing and doing it after the first publish would have cost a manual migration
on every install.

the one remaining mention of OAS is dated history in the manifest, naming what
this was ported from.
2026-08-18 01:42:37 +00:00
Claude Opus 5 ed9bf828f8 oas plugin: manifest and schema
the schema is deliberately over-specified. there are no migrations - install
generates and creates, swallowing 'already exists', so a column added in v2 is
silently absent on every machine that installed v1. email survived that only
because its table had never held a row; oas_builds holds real rows from the
first publish.

legal values are CHECK constraints rather than free text, per the house rule:
platform, signing, device kind and status. oneOf() uses sql.raw because an
interpolated value binds as a parameter and postgres refuses IN ($1, $2).

two decisions worth naming:

version is TEXT despite being numeric. it is an identifier, it appears in a
path, and 2026081801 is already at the edge of a 32-bit int. fixed width means
lexicographic sort is correct.

oas_user_keys stores the key twice - a hash to check against, and an encrypted
copy because the mobile app must READ the key back to build an install URL. the
platform's own api_keys needs no such thing because nothing re-displays them.

udid is uniquely indexed only WHERE NOT NULL: many rows sit pending without one,
but a captured identifier is one physical device and must not be claimable twice.
2026-08-18 01:16:07 +00:00