Skip to content

Commit c5e1165

Browse files
authored
Merge pull request #65 from gamesgamesgamesgamesgames/feat/update-permissioned-spaces
feat: update permissioned spaces
2 parents 3274736 + 6d20b2c commit c5e1165

53 files changed

Lines changed: 5957 additions & 879 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 48 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ wasmtime-wasi = "29"
5858
regex = "1.12.3"
5959
semver = "1.0"
6060
async-stream = "0.3.6"
61+
blake3 = "1"
62+
hkdf = "0.12"
63+
hmac = "0.12"
6164

6265
[[bin]]
6366
name = "migrate-lua-sql"
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
-- Proposal 0016: Permissioned Data alignment
2+
-- Restructures spaces for the formal AT Protocol permissioned data spec.
3+
4+
-- 1. Rename owner_did → authority_did, add creator_did
5+
ALTER TABLE happyview_spaces RENAME COLUMN owner_did TO authority_did;
6+
ALTER TABLE happyview_spaces ADD COLUMN creator_did TEXT;
7+
UPDATE happyview_spaces SET creator_did = authority_did WHERE creator_did IS NULL;
8+
ALTER TABLE happyview_spaces ALTER COLUMN creator_did SET NOT NULL;
9+
10+
-- 2. Replace access_mode + allowlist/denylist with mint_policy + app_access
11+
-- mint_policy: 'member-list' (default) | 'public' | 'managing-app'
12+
ALTER TABLE happyview_spaces ADD COLUMN mint_policy TEXT NOT NULL DEFAULT 'member-list';
13+
-- app_access: JSON open union, e.g. {"type": "open"} or {"type": "allowList", "allowed": [...]}
14+
ALTER TABLE happyview_spaces ADD COLUMN app_access TEXT NOT NULL DEFAULT '{"type":"open"}';
15+
-- Migrate existing data
16+
UPDATE happyview_spaces
17+
SET app_access = '{"type":"allowList","allowed":' || COALESCE(app_allowlist, '[]') || '}'
18+
WHERE access_mode = 'default_deny' AND app_allowlist IS NOT NULL;
19+
-- Drop old columns
20+
ALTER TABLE happyview_spaces DROP COLUMN access_mode;
21+
ALTER TABLE happyview_spaces DROP COLUMN app_allowlist;
22+
ALTER TABLE happyview_spaces DROP COLUMN app_denylist;
23+
24+
-- 3. Per-user repo state: LtHash state + signed commit
25+
CREATE TABLE happyview_space_repo_state (
26+
id TEXT PRIMARY KEY,
27+
space_id TEXT NOT NULL REFERENCES happyview_spaces(id) ON DELETE CASCADE,
28+
author_did TEXT NOT NULL,
29+
lthash_state BYTEA NOT NULL DEFAULT decode(repeat('00', 2048), 'hex'),
30+
rev TEXT,
31+
hash BYTEA,
32+
ikm BYTEA,
33+
sig BYTEA,
34+
mac BYTEA,
35+
updated_at TEXT NOT NULL,
36+
UNIQUE (space_id, author_did)
37+
);
38+
CREATE INDEX idx_space_repo_state_space ON happyview_space_repo_state(space_id);
39+
40+
-- 4. Record operation log
41+
CREATE TABLE happyview_space_record_oplog (
42+
id TEXT PRIMARY KEY,
43+
space_id TEXT NOT NULL REFERENCES happyview_spaces(id) ON DELETE CASCADE,
44+
author_did TEXT NOT NULL,
45+
rev TEXT NOT NULL,
46+
idx INTEGER NOT NULL DEFAULT 0,
47+
action TEXT NOT NULL CHECK (action IN ('create', 'update', 'delete')),
48+
collection TEXT NOT NULL,
49+
rkey TEXT NOT NULL,
50+
cid TEXT,
51+
prev TEXT,
52+
created_at TEXT NOT NULL
53+
);
54+
CREATE INDEX idx_space_oplog_space_author ON happyview_space_record_oplog(space_id, author_did);
55+
CREATE INDEX idx_space_oplog_rev ON happyview_space_record_oplog(space_id, author_did, rev);
56+
57+
-- 5. Write notification registrations
58+
CREATE TABLE happyview_space_notify_registrations (
59+
id TEXT PRIMARY KEY,
60+
space_id TEXT NOT NULL REFERENCES happyview_spaces(id) ON DELETE CASCADE,
61+
author_did TEXT,
62+
endpoint TEXT NOT NULL,
63+
registered_by TEXT NOT NULL,
64+
expires_at TEXT NOT NULL,
65+
created_at TEXT NOT NULL
66+
);
67+
CREATE INDEX idx_space_notify_space ON happyview_space_notify_registrations(space_id);
68+
CREATE INDEX idx_space_notify_repo ON happyview_space_notify_registrations(space_id, author_did);
69+
70+
-- 6. Drop old sync state table
71+
DROP TABLE IF EXISTS happyview_space_sync_state;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CREATE TABLE happyview_verification_methods (
2+
id TEXT PRIMARY KEY,
3+
fragment_id TEXT NOT NULL UNIQUE,
4+
key_type TEXT NOT NULL DEFAULT 'Multikey',
5+
public_key_multibase TEXT NOT NULL,
6+
private_key_enc BYTEA NOT NULL,
7+
created_at TEXT NOT NULL
8+
);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
-- Proposal 0016: Permissioned Data alignment
2+
3+
-- 1+2. Rebuild happyview_spaces with renamed/new columns
4+
CREATE TABLE happyview_spaces_new (
5+
id TEXT PRIMARY KEY,
6+
did TEXT NOT NULL,
7+
authority_did TEXT NOT NULL,
8+
creator_did TEXT NOT NULL,
9+
type_nsid TEXT NOT NULL,
10+
skey TEXT NOT NULL,
11+
display_name TEXT,
12+
description TEXT,
13+
mint_policy TEXT NOT NULL DEFAULT 'member-list',
14+
app_access TEXT NOT NULL DEFAULT '{"type":"open"}',
15+
managing_app_did TEXT,
16+
config TEXT NOT NULL DEFAULT '{}',
17+
revision TEXT,
18+
created_at TEXT NOT NULL,
19+
updated_at TEXT NOT NULL,
20+
UNIQUE (did, type_nsid, skey)
21+
);
22+
23+
INSERT INTO happyview_spaces_new (id, did, authority_did, creator_did, type_nsid, skey, display_name, description, mint_policy, app_access, managing_app_did, config, revision, created_at, updated_at)
24+
SELECT id, did, owner_did, owner_did, type_nsid, skey, display_name, description,
25+
'member-list',
26+
CASE
27+
WHEN access_mode = 'default_deny' AND app_allowlist IS NOT NULL
28+
THEN '{"type":"allowList","allowed":' || app_allowlist || '}'
29+
ELSE '{"type":"open"}'
30+
END,
31+
managing_app_did, config, revision, created_at, updated_at
32+
FROM happyview_spaces;
33+
34+
DROP TABLE happyview_spaces;
35+
ALTER TABLE happyview_spaces_new RENAME TO happyview_spaces;
36+
37+
-- 3. Per-user repo state
38+
CREATE TABLE happyview_space_repo_state (
39+
id TEXT PRIMARY KEY,
40+
space_id TEXT NOT NULL REFERENCES happyview_spaces(id) ON DELETE CASCADE,
41+
author_did TEXT NOT NULL,
42+
lthash_state BLOB NOT NULL DEFAULT (zeroblob(2048)),
43+
rev TEXT,
44+
hash BLOB,
45+
ikm BLOB,
46+
sig BLOB,
47+
mac BLOB,
48+
updated_at TEXT NOT NULL,
49+
UNIQUE (space_id, author_did)
50+
);
51+
CREATE INDEX idx_space_repo_state_space ON happyview_space_repo_state(space_id);
52+
53+
-- 4. Record operation log
54+
CREATE TABLE happyview_space_record_oplog (
55+
id TEXT PRIMARY KEY,
56+
space_id TEXT NOT NULL REFERENCES happyview_spaces(id) ON DELETE CASCADE,
57+
author_did TEXT NOT NULL,
58+
rev TEXT NOT NULL,
59+
idx INTEGER NOT NULL DEFAULT 0,
60+
action TEXT NOT NULL CHECK (action IN ('create', 'update', 'delete')),
61+
collection TEXT NOT NULL,
62+
rkey TEXT NOT NULL,
63+
cid TEXT,
64+
prev TEXT,
65+
created_at TEXT NOT NULL
66+
);
67+
CREATE INDEX idx_space_oplog_space_author ON happyview_space_record_oplog(space_id, author_did);
68+
CREATE INDEX idx_space_oplog_rev ON happyview_space_record_oplog(space_id, author_did, rev);
69+
70+
-- 5. Write notification registrations
71+
CREATE TABLE happyview_space_notify_registrations (
72+
id TEXT PRIMARY KEY,
73+
space_id TEXT NOT NULL REFERENCES happyview_spaces(id) ON DELETE CASCADE,
74+
author_did TEXT,
75+
endpoint TEXT NOT NULL,
76+
registered_by TEXT NOT NULL,
77+
expires_at TEXT NOT NULL,
78+
created_at TEXT NOT NULL
79+
);
80+
CREATE INDEX idx_space_notify_space ON happyview_space_notify_registrations(space_id);
81+
CREATE INDEX idx_space_notify_repo ON happyview_space_notify_registrations(space_id, author_did);
82+
83+
-- 6. Drop old sync state table
84+
DROP TABLE IF EXISTS happyview_space_sync_state;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CREATE TABLE happyview_verification_methods (
2+
id TEXT PRIMARY KEY,
3+
fragment_id TEXT NOT NULL UNIQUE,
4+
key_type TEXT NOT NULL DEFAULT 'Multikey',
5+
public_key_multibase TEXT NOT NULL,
6+
private_key_enc BLOB NOT NULL,
7+
created_at TEXT NOT NULL
8+
);

packages/docs/content/docs/api-reference/admin/admin-api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ The admin API lets you manage lexicons, monitor records, run backfill jobs, and
66

77
## Auth
88

9-
The admin API supports two authentication methods:
9+
The admin API supports three authentication methods:
1010

1111
1. **API keys** — read/write tokens starting with `hv_`, passed as `Authorization: Bearer hv_...`. See the [API Keys guide](../../guides/api-keys.md) for details.
1212
2. **Service auth JWT** — atproto inter-service authentication via signed JWTs.
13+
3. **Cookie-based session auth** — signed session cookies set during the dashboard OAuth login flow. The [web dashboard](../../getting-started/dashboard.md) uses this method.
1314

1415
In all cases the resolved DID is checked against the `users` table, and the user's permissions are loaded to authorize the request.
1516

packages/docs/content/docs/api-reference/admin/settings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Returns all key/value pairs stored in the `instance_settings` table, plus any en
7474
| `backfill_concurrent_resolution` | `BACKFILL_CONCURRENT_RESOLUTION` | `100` | How many DID document lookups to run in parallel during PDS resolution |
7575
| `backfill_retention_days` | `BACKFILL_RETENTION_DAYS` | `28` | Days to keep per-repo detail data from completed backfill jobs. `0` = keep indefinitely |
7676
| `verbose_event_logging` | `VERBOSE_EVENT_LOGGING` | `false` | Log every record index, hook execution, and hook skip to the event log. High write volume — recommended only for debugging |
77+
| `feature.spaces_enabled` | `FEATURE_SPACES_ENABLED` | --- | Enables the experimental Permissioned Spaces API. When `"true"`, space endpoints are available. When absent or any other value, space endpoints return `404 FeatureDisabled` |
7778

7879
## Upsert a setting
7980

packages/docs/content/docs/api-reference/xrpc-api.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,20 @@ If a query or procedure lexicon has a [Lua script](../guides/lua-scripting.md) a
88

99
## Auth
1010

11-
- **Queries** (`GET /xrpc/{method}`): unauthenticated
12-
- **Procedures** (`POST /xrpc/{method}`): require DPoP authentication (`Authorization: DPoP` + `DPoP` proof header + `X-Client-Key`)
11+
XRPC routes accept several authentication methods:
12+
13+
- **DPoP auth**`Authorization: DPoP <token>` + `DPoP` proof header + `X-Client-Key`
14+
- **Space credentials**`Authorization: Bearer <space_credential_jwt>` (space-scoped routes only)
15+
- **Service auth JWTs**`Authorization: Bearer <service_auth_jwt>` (inter-service calls)
16+
- **Cookie-based session auth** — signed session cookies (used by the dashboard, falls back when no `Authorization` header is present)
17+
- **Anonymous** — no auth headers (identity is `nil` in Lua scripts)
18+
19+
Bearer API keys (`hv_*`) are rejected on XRPC routes — they are only accepted on the [admin API](admin/admin-api.md).
20+
21+
Default auth behavior:
22+
23+
- **Queries** (`GET /xrpc/{method}`): unauthenticated by default (identity available if provided)
24+
- **Procedures** (`POST /xrpc/{method}`): require authentication (DPoP, session cookie, or service auth)
1325
- **getProfile**: requires auth
1426
- **uploadBlob**: requires auth
1527

0 commit comments

Comments
 (0)