auth/permit_offer_schema.ts

Permit offer DDL, types, and client-safe schemas.

An offer is a pending grant awaiting recipient consent. Lifecycle states are mutually exclusive via a CHECK constraint (permit_offer_single_terminal): at most one of accepted_at / declined_at / retracted_at may be set. On accept, the offer's resulting_permit_id links to the permit row produced by query_accept_offer.

Declarations
#

11 declarations

view source

CreatePermitOfferInput
#

PERMIT_OFFER_DEFAULT_TTL_MS
#

PERMIT_OFFER_INBOX_INDEX
#

auth/permit_offer_schema.ts view source

"\nCREATE INDEX IF NOT EXISTS permit_offer_inbox\n ON permit_offer (to_account_id, expires_at)\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL"

Inbox lookup — pending offers for an account, ordered by soonest expiry.

PERMIT_OFFER_MESSAGE_LENGTH_MAX
#

PERMIT_OFFER_PENDING_UNIQUE_INDEX
#

auth/permit_offer_schema.ts view source

"\nCREATE UNIQUE INDEX IF NOT EXISTS permit_offer_pending_unique\n ON permit_offer (\n to_account_id,\n role,\n COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'::uuid),\n from_actor_id\n )\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND supersed...

At most one pending offer per (to_account, role, scope, from_actor).

Including from_actor_id in the tuple lets multiple grantors coexist — teacher A and teacher B can each have a pending classroom_student offer for the same student and scope. A same-grantor re-offer upserts the existing pending row. COALESCE collapses NULL scopes into the sentinel UUID so Postgres's NULL-in-unique-index quirk does not allow duplicate global pending offers. The ON CONFLICT target in query_permit_offer_create must match this expression literally.

PERMIT_OFFER_SCHEMA
#

auth/permit_offer_schema.ts view source

"\nCREATE TABLE IF NOT EXISTS permit_offer (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,\n to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n role TEXT NOT NULL,\n scope_id UUID NULL,\n message TEXT NULL,\n created...

PERMIT_OFFER_SCOPE_SENTINEL_UUID
#

PermitOffer
#

auth/permit_offer_schema.ts view source

PermitOffer

Permit offer row as returned by the database.

id

type Uuid

from_actor_id

type Uuid

to_account_id

type Uuid

role

type string

scope_id

type Uuid | null

message

type string | null

created_at

type string

expires_at

type string

accepted_at

type string | null

declined_at

type string | null

decline_reason

type string | null

retracted_at

type string | null

superseded_at

Set when the offer was obsoleted by an external event — a sibling offer was accepted (yielding the permit this offer's role+scope maps to) or the resulting permit for this (to_account, role, scope) was revoked. Closes the "accept a pre-revoke offer to bypass the revoke" path.

type string | null

resulting_permit_id

type Uuid | null

PermitOfferJson
#

auth/permit_offer_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 10 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>

Zod schema for client-safe permit offer data.

SupersededOffer
#

auth/permit_offer_schema.ts view source

SupersededOffer

A superseded offer row annotated with the grantor's account_id.

Carried by superseded_offers in accept/revoke query results so callers can fan out permit_offer_supersede notifications to the grantor's sockets without a second round-trip. Populated via a CTE join on actor in the supersede UPDATE.

inheritance

extends:

from_account_id

type Uuid

to_permit_offer_json
#

auth/permit_offer_schema.ts view source

(offer: PermitOffer): { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; ... 10 more ...; resulting_permit_id: (string & $brand<...>) | null; }

Convert a PermitOffer row to its JSON payload shape.

offer

returns

{ id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<"Uuid">) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }

Depends on
#

Imported by
#