Skip to content

Commit 6cd885f

Browse files
committed
store signing keys as ids
1 parent 2a4383b commit 6cd885f

File tree

11 files changed

+66
-13
lines changed

11 files changed

+66
-13
lines changed

packages/ozone/src/context.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
} from './communication-service/template'
1717
import { AuthVerifier } from './auth-verifier'
1818
import { ImageInvalidator } from './image-invalidator'
19+
import { getSigningKeyId } from './util'
1920

2021
export type AppContextOptions = {
2122
db: Database
@@ -48,6 +49,7 @@ export class AppContext {
4849
poolIdleTimeoutMs: cfg.db.poolIdleTimeoutMs,
4950
})
5051
const signingKey = await Secp256k1Keypair.import(secrets.signingKeyHex)
52+
const signingKeyId = await getSigningKeyId(db, signingKey.did())
5153
const appviewAgent = new AtpAgent({ service: cfg.appview.url })
5254
const pdsAgent = cfg.pds
5355
? new AtpAgent({ service: cfg.pds.url })
@@ -72,6 +74,7 @@ export class AppContext {
7274

7375
const modService = ModerationService.creator(
7476
signingKey,
77+
signingKeyId,
7578
cfg,
7679
backgroundQueue,
7780
idResolver,
@@ -195,5 +198,4 @@ export class AppContext {
195198
}
196199
}
197200
}
198-
199201
export default AppContext

packages/ozone/src/daemon/context.ts

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { EventReverser } from './event-reverser'
88
import { ModerationService, ModerationServiceCreator } from '../mod-service'
99
import { BackgroundQueue } from '../background'
1010
import { IdResolver } from '@atproto/identity'
11+
import { getSigningKeyId } from '../util'
1112

1213
export type DaemonContextOptions = {
1314
db: Database
@@ -31,6 +32,7 @@ export class DaemonContext {
3132
schema: cfg.db.postgresSchema,
3233
})
3334
const signingKey = await Secp256k1Keypair.import(secrets.signingKeyHex)
35+
const signingKeyId = await getSigningKeyId(db, signingKey.did())
3436

3537
const appviewAgent = new AtpAgent({ service: cfg.appview.url })
3638
const createAuthHeaders = (aud: string) =>
@@ -52,6 +54,7 @@ export class DaemonContext {
5254

5355
const modService = ModerationService.creator(
5456
signingKey,
57+
signingKeyId,
5558
cfg,
5659
backgroundQueue,
5760
idResolver,

packages/ozone/src/db/migrations/20240228T003647759Z-add-label-sigs.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ export async function up(db: Kysely<unknown>): Promise<void> {
77
.execute()
88
await db.schema
99
.alterTable('label')
10-
.addColumn('signingKey', 'varchar')
10+
.addColumn('signingKeyId', 'integer')
11+
.execute()
12+
await db.schema
13+
.createTable('signing_key')
14+
.addColumn('id', 'serial', (col) => col.primaryKey())
15+
.addColumn('key', 'varchar', (col) => col.notNull().unique())
1116
.execute()
1217
}
1318

1419
export async function down(db: Kysely<unknown>): Promise<void> {
20+
await db.schema.dropTable('signing_key')
1521
await db.schema.alterTable('label').dropColumn('sig').execute()
1622
await db.schema.alterTable('label').dropColumn('signingKey').execute()
1723
}

packages/ozone/src/db/schema/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import * as repoPushEvent from './repo_push_event'
55
import * as recordPushEvent from './record_push_event'
66
import * as blobPushEvent from './blob_push_event'
77
import * as label from './label'
8+
import * as signingKey from './signing_key'
89
import * as communicationTemplate from './communication_template'
910

1011
export type DatabaseSchemaType = modEvent.PartialDB &
1112
modSubjectStatus.PartialDB &
1213
label.PartialDB &
14+
signingKey.PartialDB &
1315
repoPushEvent.PartialDB &
1416
recordPushEvent.PartialDB &
1517
blobPushEvent.PartialDB &

packages/ozone/src/db/schema/label.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface Label {
1111
neg: boolean
1212
cts: string
1313
sig: Buffer | null
14-
signingKey: string | null
14+
signingKeyId: number | null
1515
}
1616

1717
export type LabelRow = Selectable<Label>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Generated } from 'kysely'
2+
3+
export const tableName = 'signing_key'
4+
5+
export interface SigningKey {
6+
id: Generated<number>
7+
key: string
8+
}
9+
10+
export type PartialDB = { [tableName]: SigningKey }

packages/ozone/src/mod-service/index.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export class ModerationService {
5858
constructor(
5959
public db: Database,
6060
public signingKey: Keypair,
61+
public signingKeyId: number,
6162
public cfg: OzoneConfig,
6263
public backgroundQueue: BackgroundQueue,
6364
public idResolver: IdResolver,
@@ -69,6 +70,7 @@ export class ModerationService {
6970

7071
static creator(
7172
signingKey: Keypair,
73+
signingKeyId: number,
7274
cfg: OzoneConfig,
7375
backgroundQueue: BackgroundQueue,
7476
idResolver: IdResolver,
@@ -81,6 +83,7 @@ export class ModerationService {
8183
new ModerationService(
8284
db,
8385
signingKey,
86+
signingKeyId,
8487
cfg,
8588
backgroundQueue,
8689
idResolver,
@@ -91,8 +94,12 @@ export class ModerationService {
9194
)
9295
}
9396

94-
views = new ModerationViews(this.db, this.signingKey, this.appviewAgent, () =>
95-
this.createAuthHeaders(this.cfg.appview.did),
97+
views = new ModerationViews(
98+
this.db,
99+
this.signingKey,
100+
this.signingKeyId,
101+
this.appviewAgent,
102+
() => this.createAuthHeaders(this.cfg.appview.did),
96103
)
97104

98105
async getEvent(id: number): Promise<ModerationEventRow | undefined> {
@@ -898,8 +905,7 @@ export class ModerationService {
898905
const signedLabels = await Promise.all(
899906
labels.map((l) => signLabel(l, this.signingKey)),
900907
)
901-
const signingKey = this.signingKey.did()
902-
const dbVals = signedLabels.map((l) => formatLabelRow(l, signingKey))
908+
const dbVals = signedLabels.map((l) => formatLabelRow(l, this.signingKeyId))
903909
const { ref } = this.db.db.dynamic
904910
await sql`notify ${ref(LabelChannel)}`.execute(this.db.db)
905911
const excluded = (col: string) => ref(`excluded.${col}`)

packages/ozone/src/mod-service/util.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const formatLabel = (row: LabelRow): Label => {
2020

2121
export const formatLabelRow = (
2222
label: Label,
23-
signingKey?: string,
23+
signingKeyId?: number,
2424
): Omit<LabelRow, 'id'> => {
2525
return {
2626
src: label.src,
@@ -30,7 +30,7 @@ export const formatLabelRow = (
3030
neg: !!label.neg,
3131
cts: label.cts,
3232
sig: label.sig ? Buffer.from(label.sig) : null,
33-
signingKey: signingKey ?? null,
33+
signingKeyId: signingKeyId ?? null,
3434
}
3535
}
3636

packages/ozone/src/mod-service/views.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export class ModerationViews {
4040
constructor(
4141
private db: Database,
4242
private signingKey: Keypair,
43+
private signingKeyId: number,
4344
private appviewAgent: AtpAgent,
4445
private appviewAuth: () => Promise<AuthHeaders>,
4546
) {}
@@ -419,16 +420,15 @@ export class ModerationViews {
419420
}
420421

421422
async formatLabelAndEnsureSig(row: LabelRow) {
422-
const signingKey = this.signingKey.did()
423423
const formatted = formatLabel(row)
424-
if (!!row.sig && row.signingKey === signingKey) {
424+
if (!!row.sig && row.signingKeyId === this.signingKeyId) {
425425
return formatted
426426
}
427427
const signed = await signLabel(formatted, this.signingKey)
428428
try {
429429
await this.db.db
430430
.updateTable('label')
431-
.set({ sig: Buffer.from(signed.sig), signingKey })
431+
.set({ sig: Buffer.from(signed.sig), signingKeyId: this.signingKeyId })
432432
.where('id', '=', row.id)
433433
.execute()
434434
} catch (err) {

packages/ozone/src/util.ts

+21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
import { AxiosError } from 'axios'
22
import { XRPCError, ResponseType } from '@atproto/xrpc'
33
import { RetryOptions, retry } from '@atproto/common'
4+
import Database from './db'
5+
6+
export const getSigningKeyId = async (
7+
db: Database,
8+
signingKey: string,
9+
): Promise<number> => {
10+
const selectRes = await db.db
11+
.selectFrom('signing_key')
12+
.selectAll()
13+
.where('key', '=', signingKey)
14+
.executeTakeFirst()
15+
if (selectRes) {
16+
return selectRes.id
17+
}
18+
const insertRes = await db.db
19+
.insertInto('signing_key')
20+
.values({ key: signingKey })
21+
.returningAll()
22+
.executeTakeFirstOrThrow()
23+
return insertRes.id
24+
}
425

526
export async function retryHttp<T>(
627
fn: () => Promise<T>,

packages/ozone/tests/query-labels.test.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
OutputSchema as LabelMessage,
1111
isLabels,
1212
} from '../src/lexicon/types/com/atproto/label/subscribeLabels'
13+
import { getSigningKeyId } from '../src/util'
1314

1415
describe('ozone query labels', () => {
1516
let network: TestNetwork
@@ -154,9 +155,11 @@ describe('ozone query labels', () => {
154155

155156
const modSrvc = ctx.modService(ctx.db)
156157
const newSigningKey = await Secp256k1Keypair.create()
158+
const newSigningKeyId = await getSigningKeyId(ctx.db, newSigningKey.did())
157159
ctx.devOverride({
158160
modService: ModerationService.creator(
159161
newSigningKey,
162+
newSigningKeyId,
160163
ctx.cfg,
161164
modSrvc.backgroundQueue,
162165
ctx.idResolver,
@@ -186,7 +189,7 @@ describe('ozone query labels', () => {
186189
await network.ozone.processAll()
187190

188191
const fromDb = await ctx.db.db.selectFrom('label').selectAll().execute()
189-
expect(fromDb.every((row) => row.signingKey === newSigningKey.did())).toBe(
192+
expect(fromDb.every((row) => row.signingKeyId === newSigningKeyId)).toBe(
190193
true,
191194
)
192195

0 commit comments

Comments
 (0)