a tool to help your Letta AI agents navigate bluesky

Add AT Protocol schema publication infrastructure

This commit implements a complete solution for publishing the
studio.voyager.account.autonomy lexicon schema to AT Protocol.

Changes:
- Add scripts/publishSchema.ts: Script to publish schema to PDS
- Add scripts/README.md: Documentation for schema publication
- Add SCHEMA.md: Complete schema documentation
- Update README.md: Add AI transparency declaration section
- Update utils/declaration.ts: Add detailed comments explaining schema vs records
- Update mount.ts: Add logging for autonomy declaration creation
- Update deno.json: Add publish-schema task

The schema defines a standardized way for AI agents to declare their
automation practices for transparency. The schema is published once
by voyager.studio (domain owner), while each template user creates
their own autonomy declaration record using the published schema.

This follows the AT Protocol lexicon resolution spec:
https://atproto.com/specs/lexicon#lexicon-publication-and-resolution

Claude b90ff4d4 9bb62a37

+16
README.md
··· 24 24 - agent can be configured to _perform_: likes, posts, replies, reposts, quotes, following, blocking, muting (or undoing many of those) 25 25 - agent can also search bluesky and mute specific posts/threads to disengage 26 26 27 + ## AI transparency declaration 28 + 29 + This template automatically creates an **autonomy declaration record** in your agent's Bluesky PDS using the `studio.voyager.account.autonomy` schema. This is a standardized way for AI agents to transparently declare: 30 + 31 + - Their level of automation 32 + - Use of generative AI 33 + - Who is responsible for the account 34 + - What external services are being used 35 + 36 + **How it works:** 37 + - The **schema** is published once by voyager.studio (the template maintainer) 38 + - Your agent creates its own **record** using this schema when you run `deno task mount` 39 + - The record lives in your agent's PDS and is discoverable by other AT Protocol services 40 + 41 + This promotes transparency and accountability for AI agents on Bluesky. For more details, see [SCHEMA.md](./SCHEMA.md). 42 + 27 43 ## configurable variables: 28 44 ### required 29 45 - **`LETTA_API_KEY`**: your [letta API key](https://app.letta.com/api-keys).
+144
SCHEMA.md
··· 1 + # AI Agent Autonomy Declaration Schema 2 + 3 + ## Overview 4 + 5 + The `studio.voyager.account.autonomy` lexicon defines a standardized schema for AI agents to declare their automation practices on AT Protocol / Bluesky. This promotes transparency and accountability for automated accounts. 6 + 7 + ## Schema Details 8 + 9 + - **NSID**: `studio.voyager.account.autonomy` 10 + - **Type**: Record 11 + - **Authority**: `account.voyager.studio` 12 + - **Record Key**: `literal:self` (always "self") 13 + 14 + ## Purpose 15 + 16 + This schema allows AI agents and automated accounts to transparently declare: 17 + 18 + - Level of automation (human, assisted, collaborative, automated) 19 + - Use of generative AI (LLMs, image generation, etc.) 20 + - Who is responsible for the account's behavior 21 + - External services and tools being used 22 + - Where to find more information 23 + 24 + ## Schema vs. Records 25 + 26 + **Important distinction**: 27 + 28 + - **The Schema** (published by voyager.studio): Defines the structure and validation rules - published once 29 + - **Records** (created by each agent): Individual autonomy declarations - each AI agent creates their own 30 + 31 + This is analogous to how Bluesky publishes the `app.bsky.feed.post` schema once, but millions of users create their own post records. 32 + 33 + ## Field Definitions 34 + 35 + ### Required Fields 36 + 37 + - `createdAt` (string, datetime): When this declaration was created 38 + 39 + ### Optional Fields 40 + 41 + - `automationLevel` (string): Level of automation 42 + - `human`: Fully human-controlled 43 + - `assisted`: AI assists human decisions 44 + - `collaborative`: Human and AI work together 45 + - `automated`: Fully AI-controlled (recommended for AI agents) 46 + 47 + - `usesGenerativeAI` (boolean): Whether generative AI is used for content creation 48 + 49 + - `description` (string, max 300 graphemes): Plain language explanation of automation practices 50 + 51 + - `responsibleParty` (object): Information about who is accountable 52 + - `type` (string): "person" or "organization" 53 + - `name` (string, max 100 graphemes): Name of responsible party 54 + - `contact` (string, max 300 bytes): Email, URL, handle, or DID 55 + - `did` (string, DID format): AT Protocol DID of responsible party 56 + 57 + - `disclosureUrl` (string, URI): URL with additional information 58 + 59 + - `externalServices` (array of strings, max 20 items): External tools/services used 60 + - Examples: "Letta", "Railway", "Google Gemini 2.5-pro" 61 + 62 + ## Example Record 63 + 64 + ```json 65 + { 66 + "$type": "studio.voyager.account.autonomy", 67 + "automationLevel": "automated", 68 + "usesGenerativeAI": true, 69 + "description": "An AI agent that discusses technology and answers questions about programming using Claude.", 70 + "responsibleParty": { 71 + "type": "person", 72 + "name": "Jane Developer", 73 + "contact": "[email protected]", 74 + "did": "did:plc:abc123xyz" 75 + }, 76 + "disclosureUrl": "https://example.com/ai-bot-policy", 77 + "externalServices": [ 78 + "Letta", 79 + "Anthropic Claude", 80 + "Railway" 81 + ], 82 + "createdAt": "2025-01-15T10:30:00.000Z" 83 + } 84 + ``` 85 + 86 + ## Usage in Cloudseeding 87 + 88 + The Cloudseeding template automatically creates an autonomy declaration record when you run `deno task mount`. Configuration is done via environment variables in `.env`: 89 + 90 + - `AUTOMATION_LEVEL`: "assisted", "collaborative", or "automated" (default) 91 + - `PROJECT_DESCRIPTION`: Maps to `description` field 92 + - `RESPONSIBLE_PARTY_NAME`: Required 93 + - `RESPONSIBLE_PARTY_CONTACT`: Required 94 + - `RESPONSIBLE_PARTY_TYPE`: "person" or "organization" 95 + - `RESPONSIBLE_PARTY_BSKY`: DID or handle of responsible party 96 + - `DISCLOSURE_URL`: Optional URL to policy/disclosure page 97 + - `EXTERNAL_SERVICES`: Comma-separated list of external services 98 + 99 + ## Schema Evolution 100 + 101 + Following AT Protocol lexicon rules: 102 + 103 + - ✅ New optional fields can be added 104 + - ✅ Existing constraints can be tightened 105 + - ❌ Required fields cannot be removed 106 + - ❌ Field types cannot change 107 + - ❌ Constraints cannot be loosened 108 + 109 + Breaking changes require a new NSID (e.g., `studio.voyager.account.autonomy.v2`). 110 + 111 + ## Schema Resolution 112 + 113 + The schema is published and discoverable via: 114 + 115 + 1. **DNS TXT Record**: `_lexicon.account.voyager.studio` → DID of schema publisher 116 + 2. **PDS Record**: `com.atproto.lexicon.schema` collection in publisher's repository 117 + 3. **AT-URI**: `at://did:plc:*/com.atproto.lexicon.schema/studio.voyager.account.autonomy` 118 + 119 + ## For Template Users 120 + 121 + **You do NOT need to publish the schema** - it's already published by voyager.studio. You only need to: 122 + 123 + 1. Configure your `.env` file with your information 124 + 2. Run `deno task mount` to create YOUR autonomy declaration record 125 + 3. Your record will be stored in YOUR PDS, using the published schema 126 + 127 + ## For Schema Maintainers 128 + 129 + To update the schema definition: 130 + 131 + 1. Update `utils/declaration.ts` with schema changes 132 + 2. Ensure changes follow evolution rules 133 + 3. Run `deno task publish-schema` to publish updates 134 + 4. Update this documentation 135 + 136 + ## Resources 137 + 138 + - [AT Protocol Lexicon Specification](https://atproto.com/specs/lexicon) 139 + - [AT Protocol NSID Specification](https://atproto.com/specs/nsid) 140 + - [Cloudseeding Template Repository](https://github.com/taurean/cloudseeding) 141 + 142 + ## Contributing 143 + 144 + If you have suggestions for improving this schema, please open an issue or pull request in the Cloudseeding repository. Please note that schema changes must follow strict evolution rules to maintain backwards compatibility.
+2 -1
deno.json
··· 3 3 "config": "deno run --allow-read --allow-write setup.ts", 4 4 "mount": "deno run --allow-net --allow-env --allow-read --env mount.ts", 5 5 "watch": "deno run --allow-net --allow-env --env --watch main.ts", 6 - "start": "deno run --allow-net --allow-env --env main.ts" 6 + "start": "deno run --allow-net --allow-env --env main.ts", 7 + "publish-schema": "deno run --allow-net --allow-env --env scripts/publishSchema.ts" 7 8 }, 8 9 "imports": { 9 10 "@std/assert": "jsr:@std/assert@1",
+4
mount.ts
··· 24 24 import { searchingBlueskyMemory } from "./memories/searchingBluesky.ts"; 25 25 import { toolUseMemory } from "./memories/toolUse.ts"; 26 26 27 + // Submit autonomy declaration record to the agent's PDS for transparency 28 + // This uses the studio.voyager.account.autonomy schema published by voyager.studio 29 + console.log("📋 Creating AI autonomy declaration record..."); 27 30 await submitAutonomyDeclarationRecord(); 31 + console.log(""); 28 32 29 33 /** 30 34 * Core memory blocks that are ALWAYS attached to the agent.
+93
scripts/README.md
··· 1 + # Scripts 2 + 3 + This directory contains maintenance scripts for the Cloudseeding project. 4 + 5 + ## publishSchema.ts 6 + 7 + **Purpose**: Publishes the `studio.voyager.account.autonomy` lexicon schema to AT Protocol. 8 + 9 + **Who should run this**: Only the voyager.studio domain owner (project maintainer). 10 + 11 + **When to run this**: 12 + - Initial schema publication 13 + - When the schema definition is updated in `utils/declaration.ts` 14 + 15 + **Template users do NOT need to run this script.** The schema is already published and discoverable. Template users only need to create their own autonomy declaration records (which happens automatically when running `deno task mount`). 16 + 17 + ### Prerequisites 18 + 19 + 1. Control of the `voyager.studio` domain 20 + 2. DNS TXT record set up: 21 + ``` 22 + Name: _lexicon.account.voyager.studio 23 + Type: TXT 24 + Value: did=did:plc:YOUR_DID 25 + ``` 26 + 3. `.env` file configured with credentials for the voyager.studio account owner: 27 + - `BSKY_USERNAME`: Your voyager.studio handle 28 + - `BSKY_APP_PASSWORD`: App password for that account 29 + 30 + ### Usage 31 + 32 + ```bash 33 + deno task publish-schema 34 + ``` 35 + 36 + ### What it does 37 + 38 + 1. Logs into the voyager.studio account 39 + 2. Creates/updates a record in the `com.atproto.lexicon.schema` collection 40 + 3. Uses record key: `studio.voyager.account.autonomy` 41 + 4. Makes the schema discoverable by AT Protocol resolvers 42 + 43 + ### Schema vs. Records 44 + 45 + Understanding the distinction is important: 46 + 47 + | Schema | Records | 48 + |--------|---------| 49 + | Published once by domain owner | Created by each agent | 50 + | Defines structure and rules | Actual autonomy declarations | 51 + | Lives in voyager.studio PDS | Live in each agent's PDS | 52 + | One canonical definition | Many individual records | 53 + 54 + This is analogous to how Bluesky publishes the `app.bsky.feed.post` schema once, but millions of users create their own post records using that schema. 55 + 56 + ### Verification 57 + 58 + After publishing, you can verify: 59 + 60 + 1. **DNS Resolution**: 61 + ```bash 62 + dig TXT _lexicon.account.voyager.studio 63 + ``` 64 + 65 + 2. **Schema Record**: 66 + Visit your account's AT-URI: 67 + ``` 68 + at://YOUR_DID/com.atproto.lexicon.schema/studio.voyager.account.autonomy 69 + ``` 70 + 71 + 3. **PDS API** (if using bsky.social): 72 + ``` 73 + https://bsky.social/xrpc/com.atproto.repo.getRecord?repo=YOUR_DID&collection=com.atproto.lexicon.schema&rkey=studio.voyager.account.autonomy 74 + ``` 75 + 76 + ### Schema Evolution Rules 77 + 78 + When updating the schema, follow AT Protocol lexicon evolution rules: 79 + 80 + ✅ **Allowed**: 81 + - Adding new optional fields 82 + - Tightening existing constraints 83 + - Adding to `knownValues` arrays 84 + 85 + ❌ **Not Allowed**: 86 + - Removing required fields 87 + - Changing field types 88 + - Loosening constraints 89 + - Renaming fields 90 + 91 + Breaking changes require a new NSID (e.g., `studio.voyager.account.autonomy.v2`). 92 + 93 + See [SCHEMA.md](../SCHEMA.md) for complete schema documentation.
+119
scripts/publishSchema.ts
··· 1 + /** 2 + * Schema Publication Script 3 + * 4 + * This script publishes the studio.voyager.account.autonomy lexicon schema 5 + * to the voyager.studio PDS. This should only be run by the domain owner 6 + * to publish or update the canonical schema definition. 7 + * 8 + * Template users do NOT need to run this script - they just create records 9 + * using the published schema. 10 + * 11 + * Prerequisites: 12 + * 1. DNS TXT record: _lexicon.account.voyager.studio pointing to your DID 13 + * 2. BSKY_USERNAME and BSKY_APP_PASSWORD in .env for the voyager.studio account 14 + * 15 + * Usage: deno task publish-schema 16 + */ 17 + 18 + import { AtpAgent } from "@atproto/api"; 19 + import { AUTONOMY_DECLARATION_LEXICON } from "../utils/declaration.ts"; 20 + 21 + const publishSchema = async () => { 22 + console.log("🔧 Publishing studio.voyager.account.autonomy schema...\n"); 23 + 24 + // Get credentials from environment 25 + const username = Deno.env.get("BSKY_USERNAME"); 26 + const password = Deno.env.get("BSKY_APP_PASSWORD"); 27 + const serviceUrl = Deno.env.get("BSKY_SERVICE_URL") || "https://bsky.social"; 28 + 29 + if (!username || !password) { 30 + console.error( 31 + "❌ Error: BSKY_USERNAME and BSKY_APP_PASSWORD must be set in .env", 32 + ); 33 + console.error( 34 + " This should be the credentials for the voyager.studio account owner.\n", 35 + ); 36 + Deno.exit(1); 37 + } 38 + 39 + // Initialize agent 40 + const agent = new AtpAgent({ service: serviceUrl }); 41 + 42 + try { 43 + // Login 44 + console.log("🔐 Logging in..."); 45 + await agent.login({ 46 + identifier: username, 47 + password: password, 48 + }); 49 + console.log(`✅ Logged in as: ${agent.session?.handle} (${agent.session?.did})\n`); 50 + 51 + // Prepare schema record 52 + const schemaRecord = { 53 + $type: "com.atproto.lexicon.schema", 54 + lexicon: AUTONOMY_DECLARATION_LEXICON.lexicon, 55 + id: AUTONOMY_DECLARATION_LEXICON.id, 56 + defs: AUTONOMY_DECLARATION_LEXICON.defs, 57 + description: 58 + "Lexicon for declaring AI agent autonomy and automation practices for transparency and accountability", 59 + }; 60 + 61 + console.log(`📋 Schema Details:`); 62 + console.log(` NSID: ${schemaRecord.id}`); 63 + console.log(` Lexicon Version: ${schemaRecord.lexicon}`); 64 + console.log(` Definitions: ${Object.keys(schemaRecord.defs).join(", ")}\n`); 65 + 66 + // Check if schema already exists 67 + let existingSchema = null; 68 + try { 69 + const existing = await agent.com.atproto.repo.getRecord({ 70 + repo: agent.session?.did!, 71 + collection: "com.atproto.lexicon.schema", 72 + rkey: "studio.voyager.account.autonomy", 73 + }); 74 + existingSchema = existing.data; 75 + console.log("📄 Existing schema found - will update\n"); 76 + } catch (error: any) { 77 + if (error?.status === 400 || error?.status === 404) { 78 + console.log("📄 No existing schema found - will create new\n"); 79 + } else { 80 + throw error; 81 + } 82 + } 83 + 84 + // Publish schema 85 + console.log("📤 Publishing schema..."); 86 + const result = await agent.com.atproto.repo.putRecord({ 87 + repo: agent.session?.did!, 88 + collection: "com.atproto.lexicon.schema", 89 + rkey: "studio.voyager.account.autonomy", 90 + record: schemaRecord, 91 + }); 92 + 93 + console.log(`✅ Schema ${existingSchema ? "updated" : "published"} successfully!\n`); 94 + console.log(`📍 AT-URI: ${result.uri}`); 95 + console.log(`🔗 CID: ${result.cid}\n`); 96 + 97 + // Verify DNS setup 98 + console.log("🔍 Next steps:"); 99 + console.log(" 1. Verify DNS TXT record is set:"); 100 + console.log(" Name: _lexicon.account.voyager.studio"); 101 + console.log(" Type: TXT"); 102 + console.log(` Value: did=${agent.session?.did}`); 103 + console.log( 104 + "\n 2. Test resolution (may take time for DNS to propagate):", 105 + ); 106 + console.log( 107 + ` dig TXT _lexicon.account.voyager.studio\n`, 108 + ); 109 + console.log("✨ Schema is now discoverable by AT Protocol resolvers!"); 110 + } catch (error) { 111 + console.error("❌ Error publishing schema:", error); 112 + Deno.exit(1); 113 + } 114 + }; 115 + 116 + // Run if executed directly 117 + if (import.meta.main) { 118 + publishSchema(); 119 + }
+18
utils/declaration.ts
··· 3 3 import { Lexicons } from "@atproto/lexicon"; 4 4 import { agentContext } from "./agentContext.ts"; 5 5 6 + /** 7 + * Autonomy Declaration Lexicon Schema 8 + * 9 + * This defines the canonical schema for AI agent autonomy declarations 10 + * in AT Protocol. The schema is published at voyager.studio and is 11 + * used by all Cloudseeding template instances. 12 + * 13 + * Schema vs. Records: 14 + * - The SCHEMA is published once by voyager.studio (domain owner) 15 + * - Each agent creates their own RECORD using this schema 16 + * 17 + * Template users do NOT need to publish this schema - it's already 18 + * published and discoverable via DNS resolution. They only need to 19 + * create their own autonomy declaration record (done automatically 20 + * by submitAutonomyDeclarationRecord below). 21 + * 22 + * See SCHEMA.md for full documentation. 23 + */ 6 24 export const AUTONOMY_DECLARATION_LEXICON = { 7 25 "lexicon": 1, 8 26 "id": "studio.voyager.account.autonomy",