mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-02 10:05:41 +02:00
Phase 1 of RFC MUL-2297 — DB and credential contract for the new runtime
install flow. CLI/daemon/UI plumbing lands in later phases.
Schema (migration 091):
- daemon_token.revoked_at — explicit revoke replaces TTL-based expiry; the
exchange path now mints daemon_token rows with a ~100y expires_at so the
cleanup query stays intact while the credential is effectively long-lived
until revoked.
- install_token — short-lived (15m) single-use credential. used_at IS NULL
is the atomic gate enforced inside the UPDATE so a concurrent second
exchange returns zero rows.
API:
- POST /api/workspaces/{id}/install-tokens — admin-only mint, returns mit_
once; only the hash is stored.
- POST /api/install-tokens/exchange — public (the mit_ is the credential);
atomically consumes the install_token and returns a fresh mdt_.
Error contract for Phase 2 daemon installer:
- 401 invalid_install_token — unknown hash OR expired
- 401 install_token_already_used — hash exists but used_at IS NOT NULL
Co-authored-by: multica-agent <github@multica.ai>
27 lines
1.4 KiB
SQL
27 lines
1.4 KiB
SQL
-- install_token is a single-use, short-lived bearer credential that an
|
|
-- authorized workspace member (or autopilot, eventually) mints and pastes
|
|
-- into a daemon installer. The installer POSTs the raw token to the
|
|
-- exchange endpoint, which atomically marks it used and returns a
|
|
-- long-lived daemon_token (mdt_). Phase 1 of RFC MUL-2297 — DB +
|
|
-- credential contract only; Phase 2 wires the daemon/CLI side.
|
|
--
|
|
-- Why a dedicated table instead of reusing daemon_token / PAT:
|
|
-- * single-use semantics — used_at + UPDATE ... WHERE used_at IS NULL
|
|
-- atomically rejects replay. daemon_token has no such gate.
|
|
-- * short TTL — install tokens live ~15 minutes; daemon tokens live
|
|
-- 100 years. Sharing one row would conflate the two lifecycles.
|
|
-- * different prefix (mit_ vs mdt_) so DaemonAuth never accidentally
|
|
-- accepts a not-yet-exchanged install token on /api/daemon/*.
|
|
CREATE TABLE install_token (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
token_hash TEXT NOT NULL,
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
used_at TIMESTAMPTZ NULL
|
|
);
|
|
|
|
CREATE UNIQUE INDEX idx_install_token_hash ON install_token(token_hash);
|
|
CREATE INDEX idx_install_token_workspace ON install_token(workspace_id);
|