131 lines
10 KiB
Markdown
131 lines
10 KiB
Markdown
<!-- SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0 -->
|
|
# The convex profile
|
|
|
|
**Status: selectable preview; not production-ready.** `kit template:prune --profile convex` now
|
|
selects this app composition, and the generated Next app builds. The BFF keeps the Convex admin
|
|
key server-side, domain queries and mutations execute within real Convex function transactions,
|
|
jobs use the Convex scheduler, and the one-shot deploy service installs the app's scoped platform
|
|
runtime variables before pushing functions. The local gate deploys generated functions to a local
|
|
backend and exercises a trusted identity, tenancy, notes, the outbox audit/usage sinks, and backup
|
|
restore. It does not yet prove browser login through the generated Next BFF or a native platform
|
|
deployment. File begin/complete/delete use server-side Convex actions around transactional
|
|
mutations; streamed parts stay on the Next BFF and go directly to Blob after a Convex authorization
|
|
query. The profile gate exercises an upload, streamed part, notes attachment, detach and delete
|
|
against a local backend and fake Blob; deleting rows also have a scheduled recovery action.
|
|
Documents OCR and proposal review run through a server-side Node action and the Convex scheduler;
|
|
the local gate exercises extraction, proposal approval, and the resulting document line. The
|
|
guides and support modules' effect procedures (each one call to a platform service, owning no app
|
|
row) are generated Convex actions: `convex/lib/effects.ts` runs the kernel in the action with the
|
|
module's platform port bound from the installed deployment variables, resolves the scope and checks
|
|
the permission in internal queries over `ctx.db` (`convex/effectScope.ts`), and runs a nested read
|
|
or write as its generated query or mutation. The account module's export and deletion are
|
|
generated actions too: they compose those modules' shares and reach the deletion ledger and the
|
|
person's rows through the account store bound in the action, whose every method is one internal
|
|
query or mutation of `convex/account.ts` (the Postgres profile's own store over `ctx.db`), so the
|
|
ledger records each step as it completes, a resumption runs only the steps left, and the five-minute
|
|
`account deletion sweep` cron finishes what a module held up. Better Auth's account events reach the
|
|
same flow (`account:providerEvent`). Branding logos remain unavailable; do not treat this preview as
|
|
a production deployment.
|
|
|
|
The table describes the current and intended profile architecture from spec §5.1 and §5.4.
|
|
|
|
| | postgres (default) | convex (this profile) |
|
|
|---|---|---|
|
|
| Primary store | Postgres 18 | Convex (self-hosted, managed link) |
|
|
| Data adapter | `@conusai-kit/data-kysely` | `@conusai-kit/data-convex` (`ctx.db` repositories) |
|
|
| Procedures reach handlers via | Hono HTTP mount + `callByName` | generated `convex/generated/<module>.ts` wrappers (`pnpm convex:gen`) |
|
|
| Jobs | pg-boss (Postgres) | Convex scheduler (`convex/conusaiJobs.ts` dispatcher; job rows and dispatch share a mutation) |
|
|
| Events | outbox → pg-boss relay | outbox table → scheduled internal mutation (`convex/relay.ts`, cron every minute); audit and usage use server-side platform adapters |
|
|
| Tenant wall | repository predicate + RLS | `convex-helpers` custom functions (`convex/lib/functions.ts`) + repository predicate |
|
|
| Migrations | numbered SQL, `migrate` job | `convex/schema.ts` generated from zod, pushed by `convex deploy`; data moves use the migrations component |
|
|
| Audit | outbox subscribers over HTTP | the same subscribers, audit written via `@conusai-sdk/convex-audit` (registered in `convex/convex.config.ts`) |
|
|
| Links | `postgres`, `identity` | `convex`, `identity` |
|
|
| Process env | `POSTGRES_URL`, `POSTGRES_READER_URL`, `POSTGRES_MIGRATE_URL` | `CONVEX_SELF_HOSTED_URL`, `CONVEX_SELF_HOSTED_ADMIN_KEY` (server only) |
|
|
|
|
## What this folder carries
|
|
|
|
- `convex-deploy` runs `profiles/convex/deploy.mjs`: it writes the app-scoped platform
|
|
variables to a mode-0600 temporary file, asks the Convex CLI to install them in the backend,
|
|
then deploys the app schema and functions before web and backup start. It uses the linked project
|
|
service credential, never a human owner credential.
|
|
- `convex/filesActions.ts` performs Blob begin/complete/delete calls; `convex/files.ts` keeps each
|
|
row transition in an internal mutation. A five-minute recovery cron reconciles abandoned uploads
|
|
and reschedules durable deletes.
|
|
Streamed part bodies stay in the Next BFF and are guarded before the server-side Blob client
|
|
sends them; no file bytes or platform token enter Convex arguments or browser bundles.
|
|
- The BFF uses the same filtered module registry as the generated functions. Its readiness check
|
|
compares the deployed module and procedure list plus the entity tables, columns, and indexes.
|
|
|
|
## Work required before this can be enabled
|
|
|
|
This remains a preview because browser login through the generated Next BFF, native platform
|
|
deployment, and external object-store workflows have not passed yet. The local file and OCR flows
|
|
use a fake Blob/platform API; production readiness still requires those flows against a project-
|
|
scoped test service credential and branding workflow composition.
|
|
|
|
## What the existing tests prove
|
|
|
|
- `@conusai-kit/data-convex` tests its adapter contracts and isolation behavior under
|
|
`convex-test` (`bun run --filter @conusai-kit/data-convex test:run`).
|
|
- `scripts/vendor/test-template.sh app --profile convex` validates `conusai.json`, checks
|
|
generated Convex files for deterministic regeneration, runs the adapter package suite, and tests
|
|
an app deploy and notes/outbox/file smoke plus backup export/restore against local Convex backend
|
|
containers. After deploying locally it also typechecks the functions against Convex's generated
|
|
contexts. The smoke calls Convex functions directly with a local test identity; it does not
|
|
prove browser login or the generated Next BFF route.
|
|
- The same smoke calls the guides actions (`guides.runtime.bootstrap`, `guides.runtime.record`) and
|
|
the support actions (`support.widget.config`, `support.widgetSession.mint`) for a member against the fake
|
|
platform, checks the subject, app tenant, `role` trait and display name the platform received, and
|
|
checks that a stranger to the organization and an identity pinned to another organization get the
|
|
kernel's not-found with no platform call. The guides deploy step (code guides and anchors) still
|
|
runs only in the Postgres profile's migrate job.
|
|
- The same smoke runs the account module against the real backend and the fake platform: the
|
|
export (complete, then a failed, named support section while support is down), the deletion
|
|
status, a mismatched confirmation (no ledger row), a deletion held by support with its steps
|
|
recorded, the session revoked and the identity detached, the `account:sweep` action failing while
|
|
it waits and finishing once support is back (widget sessions revoked before the contact is
|
|
erased), a repeated request, and a ban and a deletion as provider events. In a Better Auth app it
|
|
also starts a third backend for the Better Auth deployment: a person signs up there, and their
|
|
deletion ends that account (the password then signs nobody in).
|
|
- The derived-session revocation outbox (`account.sessions.revoke`) is still the Postgres
|
|
worker's: on this profile a sign-out asks the modules directly, bounded, once.
|
|
- This profile has no Postgres worker or migration process; supported module jobs run in Convex.
|
|
The local deployed-app smoke uploads, attaches, detaches and deletes a file through the real
|
|
Convex backend and fake Blob API, and verifies OCR proposals and approval. It does not prove
|
|
browser login or the generated Next BFF route.
|
|
|
|
## Backup implementation status
|
|
|
|
The platform does **not** back a Convex backend up (decision AT-D16). The backup runner, compose
|
|
schedule, Blob retention, and restore runbook are implemented and gated with a local round-trip.
|
|
Configure `CONVEX_SELF_HOSTED_ADMIN_KEY` only in server containers and use [the backup and restore
|
|
runbook](../../docs/backup.md). Backups run outside the Convex runtime because export is an admin
|
|
CLI operation.
|
|
|
|
## Runtime variables
|
|
|
|
The web and backup services receive `CONVEX_SELF_HOSTED_URL` and
|
|
`CONVEX_SELF_HOSTED_ADMIN_KEY` from the linked Convex service. The deploy service additionally
|
|
receives `CONUSAI_API_URL`, the project's scoped `CONUSAI_API_TOKEN`, `CONUSAI_PROJECT_ID`, and
|
|
`CONUSAI_ORG_SLUG`; it copies those values into the Convex backend so file, audit, and usage actions
|
|
can reach the platform. The web process also needs `AUTH_SESSION_SECRET`, `OIDC_ISSUER`,
|
|
`OIDC_WEB_CLIENT_ID`, and `OIDC_WEB_CLIENT_SECRET`; `OIDC_SCOPES`, `OIDC_AUDIENCE`,
|
|
`AUTH_COOKIE_NAME`, `AUTH_SESSION_IDLE_SECONDS`, and `AUTH_SESSION_ABSOLUTE_SECONDS` are optional
|
|
auth preferences. `CONVEX_BACKUP_INTERVAL_HOURS` defaults to 24 and
|
|
`CONVEX_BACKUP_RETENTION` defaults to 30. The platform-injected API token remains server-side and
|
|
is never sent to browser code.
|
|
|
|
With `AUTH_PROVIDER=convex-better-auth`, Better Auth runs in a second Convex deployment of the app,
|
|
beside this one, linked to the project under the `better-auth` role (`conusai.json`:
|
|
`{"type": "convex", "role": "better-auth"}`, which `kit template:prune --auth convex-better-auth`
|
|
writes in place of the Identity link). The data deployment's `default` link injects
|
|
`CONVEX_SELF_HOSTED_URL`, `CONVEX_SITE_URL` and `CONVEX_SELF_HOSTED_ADMIN_KEY`; the Better Auth
|
|
link injects `BETTER_AUTH_CONVEX_URL` (its cloud origin), `BETTER_AUTH_CONVEX_SITE_URL` (its site
|
|
origin) and `BETTER_AUTH_CONVEX_ADMIN_KEY`, as runtime-only variables. The operator sets only
|
|
`BETTER_AUTH_SECRET` (a secret) and `AUTH_IDENTITY_NAMESPACE` as project variables (the steps are in
|
|
docs/how-to/build-an-app-on-the-template.md of the platform repository). `web` receives the two URLs and the key; `convex-deploy` receives the
|
|
URL, the key, the namespace and the secret, deploys `better-auth/` to that deployment and installs
|
|
`AUTH_PROVIDER`, `AUTH_IDENTITY_NAMESPACE`, `BETTER_AUTH_CONVEX_URL` and
|
|
`BETTER_AUTH_CONVEX_ADMIN_KEY` in this backend, whose account actions end a deleted person's
|
|
Better Auth account. None of them reaches a browser or the image build.
|