A quick update on recent work in apps/demo.
Restructuring
The api_foo.go / impl_foo.go split got pushed further — implementation files now live in api/api_impl, a separate package from the type and route definitions. The API contract is easier to browse without the implementation in the way.
Code Generator
api/api_gen parses the API package using go/packages and generates:
API.md— endpoint referenceclient.go— typed Go clientclient.js/client.d.ts— JS client with TypeScript typesopenapi.json— OpenAPI document
Generated files get a header comment marking them as such. The OpenAPI output is the most useful immediately — it's easy to throw at external tooling for testing.
HTTP Request Logger
A small middleware now logs method, path, status code, and duration for every request. /health is excluded. Nothing clever, just useful.
Audit Log
An append-only audit_log table records who did what to which resource. Entries are written in the same transaction as the mutation they describe — the log and the data can never disagree. There's no foreign key to tenants, so a Tenants_Delete entry survives the tenant.
Append has no capability check. It's a side effect; the caller can't opt out.
Covered: Tenants, TenantMembers, TenantInvitations, Grants, Roles, Groups, Bots, BotKeys, Domains.
Identity and account events (login, registration, password reset) are also recorded, but they're user-scoped rather than tenant-scoped. These entries have a Subject field — the user the event concerns — separate from Actor. On a token-based password reset, Subject is the account owner, Actor is empty. Failed logins get their own transaction since the login transaction rolls back. Unknown emails aren't recorded — no account to attach to, and recording them would let an attacker probe for accounts.
Audit_ListByUser lets users read their own trail. Reading anyone else's requires system-scoped Audit.Read.
Check out the demo app code at https://atlas9.dev/src/apps/demo/