Files

10 KiB

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. 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.