@kyleroche/relay 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,85 @@
1
+ -- OAuth 2.1 authorization server state (docs/SECURITY.md, "Access for agents"): registered clients, one-time
2
+ -- authorization codes, device codes, refresh tokens, invites, and operator settings. Access tokens are rows in `tokens`
3
+ -- (named `oauth:<client name>`), so every existing scope check keeps working; they gain an expiry and a client here.
4
+ -- Every secret-bearing column stores a SHA-256, never the plaintext, except `settings.value`, which is sealed
5
+ -- (AES-256-GCM under RELAY_SESSION_SECRET) when `sealed` is true.
6
+ ALTER TABLE tokens ADD COLUMN IF NOT EXISTS expires_at timestamptz;
7
+ ALTER TABLE tokens ADD COLUMN IF NOT EXISTS client_id text;
8
+
9
+ CREATE TABLE IF NOT EXISTS oauth_clients (
10
+ client_id text PRIMARY KEY,
11
+ client_secret_hash text, -- null for public clients (token_endpoint_auth_method = none)
12
+ name text NOT NULL,
13
+ redirect_uris text[] NOT NULL DEFAULT '{}',
14
+ grant_types text[] NOT NULL DEFAULT '{}',
15
+ token_endpoint_auth_method text NOT NULL DEFAULT 'none',
16
+ scopes text[] NOT NULL DEFAULT '{}', -- the most the client may ask for; empty = any
17
+ software_id text,
18
+ client_uri text,
19
+ created_by text NOT NULL DEFAULT '',
20
+ created_at timestamptz NOT NULL DEFAULT now()
21
+ );
22
+
23
+ CREATE TABLE IF NOT EXISTS oauth_codes (
24
+ code_hash text PRIMARY KEY,
25
+ client_id text NOT NULL REFERENCES oauth_clients(client_id) ON DELETE CASCADE,
26
+ redirect_uri text NOT NULL,
27
+ scopes text[] NOT NULL,
28
+ code_challenge text NOT NULL,
29
+ code_challenge_method text NOT NULL DEFAULT 'S256',
30
+ resource text,
31
+ actor text NOT NULL, -- human:<login> who approved on the consent page
32
+ expires_at timestamptz NOT NULL,
33
+ used_at timestamptz,
34
+ created_at timestamptz NOT NULL DEFAULT now()
35
+ );
36
+
37
+ CREATE TABLE IF NOT EXISTS oauth_device_codes (
38
+ device_code_hash text PRIMARY KEY,
39
+ user_code text NOT NULL UNIQUE, -- RLAY-4F7Q, what the person types on /activate
40
+ client_id text NOT NULL REFERENCES oauth_clients(client_id) ON DELETE CASCADE,
41
+ scopes text[] NOT NULL,
42
+ status text NOT NULL DEFAULT 'pending', -- pending | approved | denied | redeemed
43
+ actor text, -- who decided
44
+ decided_at timestamptz,
45
+ token_id text, -- tokens.id once redeemed
46
+ interval_seconds int NOT NULL DEFAULT 5,
47
+ last_polled_at timestamptz,
48
+ expires_at timestamptz NOT NULL,
49
+ created_at timestamptz NOT NULL DEFAULT now()
50
+ );
51
+ CREATE INDEX IF NOT EXISTS oauth_device_codes_pending_idx ON oauth_device_codes(expires_at) WHERE status = 'pending';
52
+
53
+ CREATE TABLE IF NOT EXISTS oauth_refresh_tokens (
54
+ token_hash text PRIMARY KEY,
55
+ client_id text NOT NULL,
56
+ access_token_id text REFERENCES tokens(id) ON DELETE SET NULL,
57
+ scopes text[] NOT NULL,
58
+ actor text NOT NULL, -- who the grant belongs to (the approver, or invite:<id>)
59
+ expires_at timestamptz NOT NULL,
60
+ rotated_from text, -- the token_hash this one replaced
61
+ used_at timestamptz, -- set when rotated; a second use revokes the lineage
62
+ revoked_at timestamptz,
63
+ created_at timestamptz NOT NULL DEFAULT now()
64
+ );
65
+
66
+ CREATE TABLE IF NOT EXISTS invites (
67
+ id text PRIMARY KEY, -- ulid
68
+ code_hash text NOT NULL UNIQUE,
69
+ name text NOT NULL, -- who it is for; the token is named oauth:<name>
70
+ scopes text[] NOT NULL,
71
+ uses_left int NOT NULL DEFAULT 1,
72
+ max_uses int NOT NULL DEFAULT 1,
73
+ expires_at timestamptz NOT NULL,
74
+ created_by text NOT NULL,
75
+ created_at timestamptz NOT NULL DEFAULT now(),
76
+ revoked_at timestamptz,
77
+ last_redeemed_at timestamptz
78
+ );
79
+
80
+ CREATE TABLE IF NOT EXISTS settings (
81
+ key text PRIMARY KEY,
82
+ value text NOT NULL,
83
+ sealed boolean NOT NULL DEFAULT false,
84
+ updated_at timestamptz NOT NULL DEFAULT now()
85
+ );
@@ -0,0 +1,31 @@
1
+ -- Workers and claims (docs/WORKERS.md): relay schedules its own tasks. A worker is a machine that ran
2
+ -- `relay work`; it registers by name, heartbeats, and claims the oldest queued task whose lane it serves. A claim
3
+ -- is a lease: it expires unless the heartbeat renews it, and an expired lease puts the task back in the queue.
4
+ -- Task state stays derived (packages/core/src/db/tasks.ts); claims only add the `claimed` step before run.started.
5
+ CREATE TABLE IF NOT EXISTS workers (
6
+ id text PRIMARY KEY, -- ulid
7
+ name text NOT NULL UNIQUE, -- `runner:<name>` is the actor on its run.* events
8
+ host text NOT NULL DEFAULT '',
9
+ lanes text[] NOT NULL DEFAULT '{}', -- claude | codex | grok, from the machine's logins
10
+ labels text[] NOT NULL DEFAULT '{}', -- extra capabilities (gpu, xcode, ...) a task may require
11
+ version text NOT NULL DEFAULT '', -- relay CLI version the worker runs
12
+ last_seen timestamptz NOT NULL DEFAULT now(),
13
+ status text NOT NULL DEFAULT 'online', -- what the worker last said: online | offline
14
+ created_at timestamptz NOT NULL DEFAULT now()
15
+ );
16
+
17
+ CREATE TABLE IF NOT EXISTS claims (
18
+ id text PRIMARY KEY, -- ulid
19
+ task_id text NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
20
+ worker_id text NOT NULL REFERENCES workers(id) ON DELETE CASCADE,
21
+ lease_until timestamptz NOT NULL,
22
+ claimed_at timestamptz NOT NULL DEFAULT now(),
23
+ released_at timestamptz,
24
+ outcome text -- finished | failed | released | expired
25
+ );
26
+ -- one live claim per task; a second worker's insert fails instead of racing
27
+ CREATE UNIQUE INDEX IF NOT EXISTS claims_live_task_idx ON claims(task_id) WHERE released_at IS NULL;
28
+ CREATE INDEX IF NOT EXISTS claims_worker_idx ON claims(worker_id, claimed_at DESC);
29
+
30
+ -- The lane a task was opened with, so the scheduler can match it without folding the feed first.
31
+ ALTER TABLE tasks ADD COLUMN IF NOT EXISTS lane text;