mirror your GitHub repos to tangled.org automatically
1

Configure Feed

Select the types of activity you want to include in your feed.

1import { Agent } from '@atproto/api' 2import type { OAuthSession } from '@atproto/oauth-client-node' 3import { sql } from 'drizzle-orm' 4import { sshKey } from '../db/schema' 5import { useDb } from './db' 6import { encrypt } from './encryption' 7import { generateKeypair } from './ssh-keypair' 8 9const PUBKEY_LEXICON = 'sh.tangled.publicKey' 10 11/** 12 * Generate a per-install SSH keypair, write the public half to the user's PDS 13 * as a `sh.tangled.publicKey` record, and persist the encrypted private half 14 * + the resulting record key in the `ssh_key` table. 15 * 16 * If a row already exists for `(installation_id, did)` we no-op. Rotation is a 17 * separate, explicit dashboard action (commit 16-ish) that re-runs this with 18 * the existing record then deletes the old one. 19 */ 20export async function generateAndPublishKey(opts: { 21 oauthSession: OAuthSession 22 installationId: number 23 keyName?: string 24}): Promise<{ created: boolean }> { 25 const db = useDb() 26 const did = opts.oauthSession.did 27 28 const existing = await db.select({ id: sshKey.id }) 29 .from(sshKey) 30 .where(sql`${sshKey.installationId} = ${opts.installationId} AND ${sshKey.did} = ${did}`) 31 if (existing.length > 0) { 32 return { created: false } 33 } 34 35 const keyName = opts.keyName ?? `synchub.to/${opts.installationId}` 36 const keypair = generateKeypair(keyName) 37 38 // Publish to PDS first. If this fails, we surface the error and leave no 39 // half-state in the DB \u2014 the caller can retry. 40 const agent = new Agent(opts.oauthSession) 41 const result = await agent.com.atproto.repo.createRecord({ 42 repo: did, 43 collection: PUBKEY_LEXICON, 44 record: { 45 $type: PUBKEY_LEXICON, 46 key: keypair.publicKeyOpenSsh, 47 name: keyName, 48 createdAt: new Date().toISOString(), 49 }, 50 }) 51 52 // Extract the rkey from the returned at-uri (`at://<did>/<collection>/<rkey>`). 53 const rkey = result.data.uri.split('/').pop() 54 if (!rkey) { 55 throw new Error(`could not parse rkey from publicKey record uri: ${result.data.uri}`) 56 } 57 58 const { ciphertext, nonce } = encrypt(keypair.privateKeyPem) 59 await db.insert(sshKey).values({ 60 installationId: opts.installationId, 61 did, 62 publicKey: keypair.publicKeyOpenSsh, 63 privateKeyCiphertext: ciphertext, 64 privateKeyNonce: nonce, 65 tangledKeyRkey: rkey, 66 }) 67 68 return { created: true } 69}