···1--- Based on https://github.com/blacksky-algorithms/rsky/blob/main/rsky-pds/src/account_manager/helpers/account.rs
2--- blacksky-algorithms/rsky is licensed under the Apache License 2.0
3--- Modified for SQLite backend
4-/* Based heavily on account-manager, did-cache, sequencer, and actor-store migrations
5- from the canonical TS implementation. */
6-7--- account-manager implementation
8--- Create App Password Table
9-CREATE TABLE IF NOT EXISTS app_password (
10- did character varying NOT NULL,
11- name character varying NOT NULL,
12- "password" character varying NOT NULL,
13- "createdAt" character varying NOT NULL
14-);
15-16-ALTER TABLE ONLY app_password
17- DROP CONSTRAINT IF EXISTS app_password_pkey;
18-ALTER TABLE ONLY app_password
19- ADD CONSTRAINT app_password_pkey PRIMARY KEY (did, name);
20-21--- Create Invite Code Table
22-CREATE TABLE IF NOT EXISTS invite_code (
23- code character varying PRIMARY KEY,
24- "availableUses" integer NOT NULL,
25- disabled smallint NOT NULL DEFAULT 0,
26- "forAccount" character varying NOT NULL,
27- "createdBy" character varying NOT NULL,
28- "createdAt" character varying NOT NULL
29-);
30-CREATE INDEX invite_code_for_user_idx
31- ON invite_code("forAccount");
32-33--- Create Invite Code Use Table
34-CREATE TABLE IF NOT EXISTS invite_code_use (
35- code character varying NOT NULL,
36- "usedBy" character varying NOT NULL,
37- "usedAt" character varying NOT NULL
38-);
39-40-ALTER TABLE ONLY invite_code_use
41- DROP CONSTRAINT IF EXISTS invite_code_use_pkey;
42-ALTER TABLE ONLY invite_code_use
43- ADD CONSTRAINT invite_code_use_pkey PRIMARY KEY (code, "usedBy");
44-45--- Create Refresh Token Table
46-CREATE TABLE IF NOT EXISTS refresh_token (
47- id character varying PRIMARY KEY,
48- did character varying NOT NULL,
49- "expiresAt" character varying NOT NULL,
50- "nextId" character varying,
51- "appPasswordName" character varying
52-);
53-CREATE INDEX refresh_token_did_idx -- Aids in refresh token cleanup
54- ON refresh_token(did);
55-56--- Create Actor Table
57-CREATE TABLE IF NOT EXISTS actor (
58- did character varying PRIMARY KEY,
59- handle character varying,
60- "createdAt" character varying NOT NULL,
61- "takedownRef" character varying,
62- "deactivatedAt" character varying,
63- "deleteAfter" character varying
64-);
65-CREATE UNIQUE INDEX actor_handle_lower_idx
66- ON actor (LOWER(handle));
67-CREATE INDEX actor_cursor_idx
68- ON actor("createdAt", did);
69-70--- Create Account Table
71-CREATE TABLE IF NOT EXISTS account (
72- did character varying PRIMARY KEY,
73- email character varying NOT NULL,
74- "recoveryKey" character varying, -- For storing Bring Your Own Key
75- "password" character varying NOT NULL,
76- "createdAt" character varying NOT NULL,
77- "invitesDisabled" smallint NOT NULL DEFAULT 0,
78- "emailConfirmedAt" character varying
79-);
80-CREATE UNIQUE INDEX account_email_lower_idx
81- ON account (LOWER(email));
82-CREATE INDEX account_cursor_idx
83- ON account("createdAt", did);
84-85--- Create Email Token Table
86-CREATE TABLE IF NOT EXISTS email_token (
87- purpose character varying NOT NULL,
88- did character varying NOT NULL,
89- token character varying NOT NULL,
90- "requestedAt" character varying NOT NULL
91-);
92-ALTER TABLE ONLY email_token
93- DROP CONSTRAINT IF EXISTS email_token_pkey;
94-ALTER TABLE ONLY email_token
95- ADD CONSTRAINT email_token_pkey PRIMARY KEY (purpose, did);
96-CREATE UNIQUE INDEX email_token_purpose_token_unique
97- ON email_token (purpose, token);
98-99-100--- actor-store implementation
101--- Create Repo Root Table
102-CREATE TABLE IF NOT EXISTS repo_root (
103- did character varying PRIMARY KEY,
104- cid character varying NOT NULL,
105- rev character varying NOT NULL,
106- "indexedAt" character varying NOT NULL
107-);
108-109--- Create Repo Block Table
110-CREATE TABLE IF NOT EXISTS repo_block (
111- cid character varying NOT NULL,
112- did character varying NOT NULL,
113- "repoRev" character varying NOT NULL,
114- size integer NOT NULL,
115- content bytea NOT NULL
116-);
117-ALTER TABLE ONLY repo_block
118- ADD CONSTRAINT repo_block_pkey PRIMARY KEY (cid, did);
119-CREATE INDEX repo_block_repo_rev_idx
120- ON repo_block("repoRev", cid);
121-122--- Create Record Table
123-CREATE TABLE IF NOT EXISTS record (
124- uri character varying PRIMARY KEY,
125- cid character varying NOT NULL,
126- did character varying NOT NULL,
127- collection character varying NOT NULL,
128- "rkey" character varying NOT NULL,
129- "repoRev" character varying,
130- "indexedAt" character varying NOT NULL,
131- "takedownRef" character varying
132-);
133-CREATE INDEX record_did_cid_idx
134- ON record(cid);
135-CREATE INDEX record_did_collection_idx
136- ON record(collection);
137-CREATE INDEX record_repo_rev_idx
138- ON record("repoRev");
139-140--- Create Blob Table
141-CREATE TABLE IF NOT EXISTS blob (
142- cid character varying NOT NULL,
143- did character varying NOT NULL,
144- "mimeType" character varying NOT NULL,
145- size integer NOT NULL,
146- "tempKey" character varying,
147- width integer,
148- height integer,
149- "createdAt" character varying NOT NULL,
150- "takedownRef" character varying
151-);
152-ALTER TABLE ONLY blob
153- ADD CONSTRAINT blob_pkey PRIMARY KEY (cid, did);
154-CREATE INDEX blob_tempkey_idx
155- ON blob("tempKey");
156-157--- Create Record Blob Table
158-CREATE TABLE IF NOT EXISTS record_blob (
159- "blobCid" character varying NOT NULL,
160- "recordUri" character varying NOT NULL,
161- did character varying NOT NULL
162-);
163-ALTER TABLE ONLY record_blob
164- DROP CONSTRAINT IF EXISTS record_blob_pkey;
165-ALTER TABLE ONLY record_blob
166- ADD CONSTRAINT record_blob_pkey PRIMARY KEY ("blobCid","recordUri");
167-168--- Create Backlink Table
169-CREATE TABLE IF NOT EXISTS backlink (
170- uri character varying NOT NULL,
171- path character varying NOT NULL,
172- "linkTo" character varying NOT NULL
173-);
174-ALTER TABLE ONLY backlink
175- DROP CONSTRAINT IF EXISTS backlink_pkey;
176-ALTER TABLE ONLY backlink
177- ADD CONSTRAINT backlink_pkey PRIMARY KEY (uri, path);
178-CREATE INDEX backlink_link_to_idx
179- ON backlink(path, "linkTo");
180-181--- Create Account Preferences Table
182-CREATE TABLE IF NOT EXISTS account_pref (
183- id SERIAL PRIMARY KEY,
184- did character varying NOT NULL,
185- name character varying NOT NULL,
186- "valueJson" text
187-);
188-189--- did-cache implementation
190--- Create DID Cache Table
191-CREATE TABLE IF NOT EXISTS did_doc (
192- did character varying PRIMARY KEY,
193- doc text NOT NULL,
194- "updatedAt" bigint NOT NULL
195-);
196-197--- sequencer implementation
198--- Create Repo Sequence Table
199-CREATE TABLE IF NOT EXISTS repo_seq (
200- seq bigserial PRIMARY KEY,
201- did character varying NOT NULL,
202- "eventType" character varying NOT NULL,
203- event bytea NOT NULL,
204- invalidated smallint NOT NULL DEFAULT 0,
205- "sequencedAt" character varying NOT NULL
206-);
207-CREATE INDEX repo_seq_did_idx -- for filtering seqs based on did
208- ON repo_seq(did);
209-CREATE INDEX repo_seq_event_type_idx -- for filtering seqs based on event type
210- ON repo_seq("eventType");
211-CREATE INDEX repo_seq_sequenced_at_index -- for entering into the seq stream at a particular time
212- ON repo_seq("sequencedAt");
213-214--- OAuth schema
215-CREATE TABLE IF NOT EXISTS authorization_request
216-(
217- id character varying NOT NULL,
218- did character varying,
219- "deviceId" character varying,
220- "clientId" character varying NOT NULL,
221- "clientAuth" character varying NOT NULL,
222- parameters character varying NOT NULL,
223- "expiresAt" Timestamptz NOT NULL,
224- code character varying
225-);
226-227-ALTER TABLE ONLY authorization_request
228- DROP CONSTRAINT IF EXISTS authorization_request_code_idx;
229-ALTER TABLE ONLY authorization_request
230- ADD CONSTRAINT authorization_request_code_idx PRIMARY KEY (id);
231-232--- TODO expires at index
233-234-CREATE TABLE IF NOT EXISTS device
235-(
236- id character varying NOT NULL,
237- "sessionId" character varying unique,
238- "userAgent" character varying,
239- "ipAddress" character varying NOT NULL,
240- "lastSeenAt" Timestamptz NOT NULL
241-);
242-243-ALTER TABLE ONLY device
244- DROP CONSTRAINT IF EXISTS pds_idx;
245-ALTER TABLE ONLY device
246- ADD CONSTRAINT pds_idx PRIMARY KEY (id);
247-248-CREATE TABLE IF NOT EXISTS device_account
249-(
250- did character varying NOT NULL,
251- "deviceId" character varying NOT NULL,
252- "authenticatedAt" Timestamptz NOT NULL,
253- remember boolean NOT NULL,
254- "authorizedClients" character varying NOT NULL
255-);
256-257-ALTER TABLE ONLY device_account
258- DROP CONSTRAINT IF EXISTS device_account_pk;
259-ALTER TABLE ONLY device_account
260- ADD CONSTRAINT device_account_pk PRIMARY KEY ("deviceId", did);
261-262---TODO add foreign key constraints
263-264-CREATE TABLE IF NOT EXISTS token
265-(
266- id character varying NOT NULL, --TODO
267- did character varying NOT NULL,
268- "tokenId" character varying NOT NULL unique,
269- "createdAt" Timestamptz NOT NULL,
270- "updatedAt" Timestamptz NOT NULL,
271- "expiresAt" Timestamptz NOT NULL,
272- "clientId" character varying NOT NULL,
273- "clientAuth" character varying NOT NULL,
274- "deviceId" character varying,
275- parameters character varying NOT NULL,
276- details character varying,
277- code character varying,
278- "currentRefreshToken" character varying unique
279-);
280-281-ALTER TABLE ONLY token
282- DROP CONSTRAINT IF EXISTS token_idx;
283-ALTER TABLE ONLY token
284- ADD CONSTRAINT token_idx PRIMARY KEY (id);
285-286-CREATE TABLE IF NOT EXISTS used_refresh_token
287-(
288- "refreshToken" character varying NOT NULL,
289- "tokenId" character varying NOT NULL
290-);
291-292-ALTER TABLE ONLY used_refresh_token
293- DROP CONSTRAINT IF EXISTS used_refresh_token_pk;
294-ALTER TABLE ONLY used_refresh_token
295- ADD CONSTRAINT used_refresh_token_pk PRIMARY KEY ("refreshToken");
···1-DROP TABLE IF EXISTS invites;
2-3-DROP TABLE IF EXISTS handles;
4-5-DROP TABLE IF EXISTS accounts;
6-7-DROP TABLE IF EXISTS sessions;
···0000000
-29
migrations/20250104202448_init.up.sql
···1-CREATE TABLE IF NOT EXISTS accounts (
2- did TEXT PRIMARY KEY NOT NULL,
3- email TEXT NOT NULL UNIQUE,
4- password TEXT NOT NULL,
5- root TEXT NOT NULL,
6- rev TEXT NOT NULL,
7- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
8-);
9-10-CREATE TABLE IF NOT EXISTS handles (
11- handle TEXT PRIMARY KEY NOT NULL,
12- did TEXT NOT NULL,
13- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
14- FOREIGN KEY (did) REFERENCES accounts(did)
15-);
16-17-CREATE TABLE IF NOT EXISTS invites (
18- id TEXT PRIMARY KEY NOT NULL,
19- did TEXT,
20- count INTEGER NOT NULL DEFAULT 1,
21- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
22-);
23-24-CREATE TABLE IF NOT EXISTS sessions (
25- id TEXT PRIMARY KEY NOT NULL,
26- did TEXT NOT NULL,
27- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
28- FOREIGN KEY (did) REFERENCES accounts(did)
29-);
···1-CREATE TABLE IF NOT EXISTS blob_ref (
2- -- N.B: There is a hidden `rowid` field inserted by sqlite.
3- cid TEXT NOT NULL,
4- did TEXT NOT NULL,
5- record TEXT
6-);
···1-CREATE TABLE IF NOT EXISTS oauth_par_requests (
2- request_uri TEXT PRIMARY KEY NOT NULL,
3- client_id TEXT NOT NULL,
4- response_type TEXT NOT NULL,
5- code_challenge TEXT NOT NULL,
6- code_challenge_method TEXT NOT NULL,
7- state TEXT,
8- login_hint TEXT,
9- scope TEXT,
10- redirect_uri TEXT,
11- response_mode TEXT,
12- display TEXT,
13- created_at INTEGER NOT NULL,
14- expires_at INTEGER NOT NULL
15-);
16-CREATE TABLE IF NOT EXISTS oauth_authorization_codes (
17- code TEXT PRIMARY KEY NOT NULL,
18- client_id TEXT NOT NULL,
19- subject TEXT NOT NULL,
20- code_challenge TEXT NOT NULL,
21- code_challenge_method TEXT NOT NULL,
22- redirect_uri TEXT NOT NULL,
23- scope TEXT,
24- created_at INTEGER NOT NULL,
25- expires_at INTEGER NOT NULL,
26- used BOOLEAN NOT NULL DEFAULT FALSE
27-);
28-CREATE TABLE IF NOT EXISTS oauth_refresh_tokens (
29- token TEXT PRIMARY KEY NOT NULL,
30- client_id TEXT NOT NULL,
31- subject TEXT NOT NULL,
32- dpop_thumbprint TEXT NOT NULL,
33- scope TEXT,
34- created_at INTEGER NOT NULL,
35- expires_at INTEGER NOT NULL,
36- revoked BOOLEAN NOT NULL DEFAULT FALSE
37-);
···0000000000000000000000000000000000000
-6
migrations/20250502032700_jti.down.sql
···1-DROP INDEX IF EXISTS idx_jtis_expires_at;
2-DROP INDEX IF EXISTS idx_refresh_tokens_expires_at;
3-DROP INDEX IF EXISTS idx_auth_codes_expires_at;
4-DROP INDEX IF EXISTS idx_par_expires_at;
5-6-DROP TABLE IF EXISTS oauth_used_jtis;
···000000
-13
migrations/20250502032700_jti.up.sql
···1--- Table for tracking used JTIs to prevent replay attacks
2-CREATE TABLE IF NOT EXISTS oauth_used_jtis (
3- jti TEXT PRIMARY KEY NOT NULL,
4- issuer TEXT NOT NULL,
5- created_at INTEGER NOT NULL,
6- expires_at INTEGER NOT NULL
7-);
8-9--- Create indexes for faster lookups and cleanup
10-CREATE INDEX IF NOT EXISTS idx_par_expires_at ON oauth_par_requests(expires_at);
11-CREATE INDEX IF NOT EXISTS idx_auth_codes_expires_at ON oauth_authorization_codes(expires_at);
12-CREATE INDEX IF NOT EXISTS idx_refresh_tokens_expires_at ON oauth_refresh_tokens(expires_at);
13-CREATE INDEX IF NOT EXISTS idx_jtis_expires_at ON oauth_used_jtis(expires_at);
···0000000000000
-16
migrations/20250508251242_actor_store.down.sql
···1--- Drop indexes
2-DROP INDEX IF EXISTS idx_backlink_link_to;
3-DROP INDEX IF EXISTS idx_blob_tempkey;
4-DROP INDEX IF EXISTS idx_record_repo_rev;
5-DROP INDEX IF EXISTS idx_record_collection;
6-DROP INDEX IF EXISTS idx_record_cid;
7-DROP INDEX IF EXISTS idx_repo_block_repo_rev;
8-9--- Drop tables
10-DROP TABLE IF EXISTS account_pref;
11-DROP TABLE IF EXISTS backlink;
12-DROP TABLE IF EXISTS record_blob;
13-DROP TABLE IF EXISTS blob;
14-DROP TABLE IF EXISTS record;
15-DROP TABLE IF EXISTS repo_block;
16-DROP TABLE IF EXISTS repo_root;
···0000000000000000
-70
migrations/20250508251242_actor_store.up.sql
···1--- Actor store schema matching TypeScript implementation
2-3--- Repository root information
4-CREATE TABLE IF NOT EXISTS repo_root (
5- did TEXT PRIMARY KEY NOT NULL,
6- cid TEXT NOT NULL,
7- rev TEXT NOT NULL,
8- indexedAt TEXT NOT NULL
9-);
10-11--- Repository blocks (IPLD blocks)
12-CREATE TABLE IF NOT EXISTS repo_block (
13- cid TEXT PRIMARY KEY NOT NULL,
14- repoRev TEXT NOT NULL,
15- size INTEGER NOT NULL,
16- content BLOB NOT NULL
17-);
18-19--- Record index
20-CREATE TABLE IF NOT EXISTS record (
21- uri TEXT PRIMARY KEY NOT NULL,
22- cid TEXT NOT NULL,
23- collection TEXT NOT NULL,
24- rkey TEXT NOT NULL,
25- repoRev TEXT NOT NULL,
26- indexedAt TEXT NOT NULL,
27- takedownRef TEXT
28-);
29-30--- Blob storage metadata
31-CREATE TABLE IF NOT EXISTS blob (
32- cid TEXT PRIMARY KEY NOT NULL,
33- mimeType TEXT NOT NULL,
34- size INTEGER NOT NULL,
35- tempKey TEXT,
36- width INTEGER,
37- height INTEGER,
38- createdAt TEXT NOT NULL,
39- takedownRef TEXT
40-);
41-42--- Record-blob associations
43-CREATE TABLE IF NOT EXISTS record_blob (
44- blobCid TEXT NOT NULL,
45- recordUri TEXT NOT NULL,
46- PRIMARY KEY (blobCid, recordUri)
47-);
48-49--- Backlinks between records
50-CREATE TABLE IF NOT EXISTS backlink (
51- uri TEXT NOT NULL,
52- path TEXT NOT NULL,
53- linkTo TEXT NOT NULL,
54- PRIMARY KEY (uri, path)
55-);
56-57--- User preferences
58-CREATE TABLE IF NOT EXISTS account_pref (
59- id INTEGER PRIMARY KEY AUTOINCREMENT,
60- name TEXT NOT NULL,
61- valueJson TEXT NOT NULL
62-);
63-64--- Create indexes
65-CREATE INDEX IF NOT EXISTS idx_repo_block_repo_rev ON repo_block(repoRev, cid);
66-CREATE INDEX IF NOT EXISTS idx_record_cid ON record(cid);
67-CREATE INDEX IF NOT EXISTS idx_record_collection ON record(collection);
68-CREATE INDEX IF NOT EXISTS idx_record_repo_rev ON record(repoRev);
69-CREATE INDEX IF NOT EXISTS idx_blob_tempkey ON blob(tempKey);
70-CREATE INDEX IF NOT EXISTS idx_backlink_link_to ON backlink(path, linkTo);
···1- CREATE TABLE IF NOT EXISTS blocks (
2- cid TEXT PRIMARY KEY NOT NULL,
3- data BLOB NOT NULL,
4- multicodec INTEGER NOT NULL,
5- multihash INTEGER NOT NULL
6- );
7- CREATE TABLE IF NOT EXISTS tree_nodes (
8- repo_did TEXT NOT NULL,
9- key TEXT NOT NULL,
10- value_cid TEXT NOT NULL,
11- PRIMARY KEY (repo_did, key),
12- FOREIGN KEY (value_cid) REFERENCES blocks(cid)
13- );
14- CREATE INDEX IF NOT EXISTS idx_blocks_cid ON blocks(cid);
15- CREATE INDEX IF NOT EXISTS idx_tree_nodes_repo ON tree_nodes(repo_did);
···000000000000000
-5
migrations/20250510222500_actor_migration.up.sql
···1-CREATE TABLE IF NOT EXISTS actor_migration (
2- id INTEGER PRIMARY KEY AUTOINCREMENT,
3- name TEXT NOT NULL,
4- appliedAt TEXT NOT NULL
5-);