Lexicon Publication Plan #
Overview #
Publish the computer.aesthetic.* lexicon schemas to ATProto according to the official specification at https://atproto.com/specs/lexicon#lexicon-publication-and-resolution
Current State #
Lexicon Schemas (5 total) #
Located in /at/lexicons/computer/aesthetic/:
painting.json- Digital paintings with thumbnails and recordingsmood.json- User mood/status updatespiece.json- Interactive piece metadatakidlisp.json- KidLisp code artifactstape.json- Video/audio tape recordings
Authority & Identity #
- Authority Domain:
aesthetic.computer - Reversed Authority:
computer.aesthetic - Main Account DID:
did:plc:k3k3wknzkcnekbnyde4dbatz(aesthetic.computer) ← Use for lexicons - Art Account DID:
did:plc:tliuubv7lyv2uiknsjbf4ppw(art.at.aesthetic.computer) - Main PDS:
https://bsky.social(Bluesky) - Custom PDS:
https://at.aesthetic.computer(for user accounts)
ATProto Lexicon Publication Requirements #
1. DNS TXT Record Setup #
According to the spec, create a DNS TXT record at:
_lexicon.aesthetic.computer
Value:
did=did:plc:k3k3wknzkcnekbnyde4dbatz
Purpose: Links the authority domain to the DID hosting the lexicon schemas
2. Schema Record Publication #
For each lexicon, publish a com.atproto.lexicon.schema record to the repository.
Record Structure:
{
"$type": "com.atproto.lexicon.schema",
"lexicon": 1,
"id": "computer.aesthetic.painting", // NSID
"defs": {
// schema definitions from painting.json
}
}
Record Key: Must be the NSID itself (e.g., computer.aesthetic.painting)
AT-URI Format:
at://did:plc:k3k3wknzkcnekbnyde4dbatz/com.atproto.lexicon.schema/computer.aesthetic.painting
3. Authority at "Group" Level #
All NSIDs with the same authority (computer.aesthetic.*) are published to a single repository (our PDS at at.aesthetic.computer).
Implementation Steps #
Phase 1: DNS Configuration #
Tool: Cloudflare DNS (credentials in vault)
- Access Cloudflare DNS for aesthetic.computer domain
- Create TXT record:
- Name:
_lexicon.aesthetic.computer - Value:
did=did:plc:k3k3wknzkcnekbnyde4dbatz - TTL: Auto (or 300 for testing)
- Name:
- Verify DNS propagation:
dig TXT _lexicon.aesthetic.computer +short
Phase 2: Create Publication Script #
File: /at/scripts/publish-lexicons.mjs
Features:
- Read all lexicon JSON files from
/at/lexicons/computer/aesthetic/ - For each lexicon:
- Load schema definitions
- Create
com.atproto.lexicon.schemarecord - Use NSID as record key
- Publish to Bluesky PDS repository
- Support dry-run mode
- Provide detailed logging
Dependencies:
@atproto/api(already in package.json)- Use existing
.envcredentials (BSKY_IDENTIFIER, BSKY_APP_PASSWORD) - Important: Uses https://bsky.social (not custom PDS) for lexicon publication
Phase 3: Publish Schemas #
- Run script in dry-run mode to verify
- Publish all 5 lexicons:
computer.aesthetic.paintingcomputer.aesthetic.moodcomputer.aesthetic.piececomputer.aesthetic.kidlispcomputer.aesthetic.tape
- Verify records exist in repository
Phase 4: Verification #
- DNS Check: Verify TXT record resolves correctly
- DID Document Check: Verify PDS service endpoint
- Record Retrieval: Query each schema record via AT-URI
- Lexicon Resolution: Test full resolution chain:
- NSID → reversed authority → DNS lookup → DID → PDS → record
Script Pseudocode #
import { BskyAgent } from '@atproto/api';
import { readdir, readFile } from 'fs/promises';
import path from 'path';
async function publishLexicons(dryRun = true) {
// 1. Initialize agent
const agent = new BskyAgent({ service: process.env.PDS_URL });
await agent.login({
identifier: process.env.BSKY_IDENTIFIER,
password: process.env.BSKY_APP_PASSWORD
});
// 2. Read lexicon files
const lexiconsDir = './lexicons/computer/aesthetic';
const files = await readdir(lexiconsDir);
for (const file of files.filter(f => f.endsWith('.json'))) {
const schemaPath = path.join(lexiconsDir, file);
const schema = JSON.parse(await readFile(schemaPath, 'utf-8'));
// 3. Create schema record
const record = {
$type: 'com.atproto.lexicon.schema',
lexicon: schema.lexicon,
id: schema.id, // NSID
defs: schema.defs
};
// 4. Publish with NSID as rkey
const rkey = schema.id;
if (dryRun) {
console.log(`[DRY RUN] Would publish ${schema.id}`);
console.log(JSON.stringify(record, null, 2));
} else {
await agent.api.com.atproto.repo.putRecord({
repo: agent.session.did,
collection: 'com.atproto.lexicon.schema',
rkey: rkey,
record: record
});
console.log(`✓ Published ${schema.id}`);
}
}
}
Testing Strategy #
1. Dry Run #
node /workspaces/aesthetic-computer/at/scripts/publish-lexicons.mjs --dry-run
2. Live Publication #
node /workspaces/aesthetic-computer/at/scripts/publish-lexicons.mjs
3. Verification Queries #
# Query a specific schema
node -e "
import('@atproto/api').then(({BskyAgent}) => {
const agent = new BskyAgent({service: 'https://bsky.social'});
agent.api.com.atproto.repo.getRecord({
repo: 'did:plc:k3k3wknzkcnekbnyde4dbatz',
collection: 'com.atproto.lexicon.schema',
rkey: 'computer.aesthetic.painting'
}).then(res => console.log(JSON.stringify(res.data, null, 2)));
});
"
Success Criteria #
References #
- ATProto Lexicon Spec
- com.atproto.lexicon.schema
- Existing sync scripts:
/system/backend/sync-atproto.mjs,/at/share-latest-painting.mjs
Notes #
- Authority is at "group" level, so all
computer.aesthetic.*NSIDs go to one repo - DNS record links the authority domain to the DID
- Record keys MUST be the NSID (not TIDs like other records)
- Schema records are special system records in the
com.atproto.lexiconnamespace - Once published, external apps can discover and use these schemas via DNS + DID resolution