/at Directory Manifest#
Purpose: ATProto (Authenticated Transfer Protocol) experimentation and Bluesky integration toolkit for Aesthetic Computer.
Created: October 6, 2025
Status: 🧪 Experimental - exploring before infrastructure commitment
Directory Structure#
/workspaces/aesthetic-computer/at/
├── README.md # Full documentation and learning resources
├── QUICKSTART.md # Quick start guide with current state
├── MANIFEST.md # This file - complete directory documentation
├── package.json # Node.js project with ATProto dependencies
├── package-lock.json # Locked dependency versions
├── .env.example # Environment variable template
├── .env # Local config (gitignored, create from example)
├── .gitignore # Git ignore patterns
├── node_modules/ # Installed dependencies (gitignored)
├── query-profile.mjs # Query ATProto profiles
├── query-posts.mjs # Fetch posts from feeds
├── post-to-bluesky.mjs # Post to @aesthetic.computer account
├── explore-lexicons.mjs # Browse lexicon schemas
├── resolve-did.mjs # Resolve DIDs and inspect PDS
└── test-all.mjs # Test suite for all tools
Dependencies#
NPM Packages (from package.json)#
| Package | Version | Purpose |
|---|---|---|
@atproto/api |
^0.17.0 | Main ATProto SDK for Bluesky |
@atproto/xrpc |
^0.6.5 | Low-level XRPC client |
@atproto/lexicon |
^0.4.2 | Lexicon schema tools |
@atproto/identity |
^0.4.3 | DID resolution and handle verification |
@atproto/syntax |
^0.3.1 | ATProto syntax utilities |
dotenv |
^16.4.5 | Environment variable management |
Total size: ~23 packages when installed
Tool Documentation#
1. query-profile.mjs#
Purpose: Query profile information for any ATProto handle or DID.
Usage:
node query-profile.mjs aesthetic.computer
node query-profile.mjs did:plc:k3k3wknzkcnekbnyde4dbatz
node query-profile.mjs bsky.app
Output:
- Handle and DID
- Display name and description
- Follower/following counts
- Post count
- Recent posts (last 5)
- Avatar and banner URLs
API Used: https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile
Authentication: None required (public endpoint)
2. query-posts.mjs#
Purpose: Fetch and display detailed post information from any account.
Usage:
node query-posts.mjs aesthetic.computer
node query-posts.mjs aesthetic.computer --limit=20
Output:
- Post text and timestamps
- Engagement metrics (likes, replies, reposts)
- Embeds (images, links, quoted posts)
- Rich text features (mentions, links, hashtags)
- Reply context
API Used: https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed
Authentication: None required (public endpoint)
3. post-to-bluesky.mjs#
Purpose: Post to the official @aesthetic.computer Bluesky account.
Usage:
# Simple text post
node post-to-bluesky.mjs "Hello from Aesthetic Computer! 🎨"
# Post with image
node post-to-bluesky.mjs "New painting!" --image=painting.png
# Post with image and alt text
node post-to-bluesky.mjs "Check this out" --image=art.png --alt="Abstract painting"
Requirements:
.envfile with credentials:BSKY_IDENTIFIER=aesthetic.computer BSKY_APP_PASSWORD=your-app-password- App password from: https://bsky.app/settings/app-passwords
Output:
- Post URI (AT-URI)
- Post CID (content identifier)
- Web URL to view on Bluesky
API Used: https://bsky.social (login + create post)
Authentication: Required (OAuth with app password)
4. explore-lexicons.mjs#
Purpose: Browse and understand ATProto lexicon schemas.
Usage:
node explore-lexicons.mjs
Output:
-
Standard Bluesky lexicons (
app.bsky.*)app.bsky.feed.post- Posts/status updatesapp.bsky.feed.like- Likes on postsapp.bsky.graph.follow- Follow relationshipsapp.bsky.embed.images- Image embedsapp.bsky.embed.external- Link embeds
-
Proposed AC custom lexicons (
computer.aesthetic.*)computer.aesthetic.painting- Paintings with rich metadatacomputer.aesthetic.piece- Interactive pieces/programscomputer.aesthetic.kidlisp- KidLisp code snippetscomputer.aesthetic.mood- Mood/emotion entries
Purpose: Educational tool to understand federation and lexicon design
5. resolve-did.mjs#
Purpose: Resolve DIDs to inspect PDS configurations and DID documents.
Usage:
node resolve-did.mjs aesthetic.computer
node resolve-did.mjs did:plc:k3k3wknzkcnekbnyde4dbatz
node resolve-did.mjs bsky.app
Output:
- DID identifier
- Verified handles (alsoKnownAs)
- PDS endpoint (where data is hosted)
- Verification methods (public keys)
- Full DID document (JSON)
API Used: https://plc.directory/{did} for did:plc:* DIDs
Authentication: None required (public PLC directory)
6. test-all.mjs#
Purpose: Quick test suite to verify all tools are working.
Usage:
node test-all.mjs
Tests:
- ✅ Profile query
- ✅ Feed query
- ⏭️ Authentication (if credentials provided)
Output: Pass/fail status for each test with helpful error messages.
Current State#
@aesthetic.computer on Bluesky#
{
"handle": "aesthetic.computer",
"did": "did:plc:k3k3wknzkcnekbnyde4dbatz",
"pds": "https://chanterelle.us-west.host.bsky.network",
"followers": 0,
"following": 2,
"posts": 1,
"created": "2024-10-20T01:54:27Z",
"lastPost": "ty the invite 🦋"
}
PDS Provider: Bluesky (third-party infrastructure)
Handle Verification: ✅ Custom domain aesthetic.computer verified via DNS
Environment Setup#
Required Environment Variables#
Create .env file (from .env.example):
# Bluesky Service (default: official PDS)
BSKY_SERVICE=https://bsky.social
# For posting to @aesthetic.computer (optional)
BSKY_IDENTIFIER=aesthetic.computer
BSKY_APP_PASSWORD=your-app-password-here
# For firehose monitoring (future)
BSKY_RELAY=wss://bsky.network
Getting App Password#
- Log in to https://bsky.app
- Go to Settings → App Passwords
- Create new app password
- Name it "Aesthetic Computer Dev" or similar
- Copy password to
.env
Security: App passwords are scoped - they cannot change account settings or delete the account.
Integration Paths#
Path 1: Simple Integration (Recommended First Step)#
Goal: Share paintings to @aesthetic.computer Bluesky account
Implementation:
// In AC's painting share handler
import { BskyAgent } from '@atproto/api'
async function sharePaintingToBluesky(painting, user) {
const agent = new BskyAgent({ service: 'https://bsky.social' })
await agent.login({
identifier: process.env.BSKY_IDENTIFIER,
password: process.env.BSKY_APP_PASSWORD
})
// Upload painting image
const imageData = await fetch(painting.url).then(r => r.arrayBuffer())
const { data } = await agent.uploadBlob(
new Uint8Array(imageData),
{ encoding: 'image/png' }
)
// Post to Bluesky
await agent.post({
text: `New painting by @${user.handle}: "${painting.title}"\n\n🎨 aesthetic.computer/${painting.slug}`,
embed: {
$type: 'app.bsky.embed.images',
images: [{
image: data.blob,
alt: painting.description || painting.title
}]
}
})
}
Benefits:
- ✅ Zero infrastructure
- ✅ Instant Bluesky presence
- ✅ Marketing and showcase
- ✅ Test federation
- ✅ Learn ATProto
Drawbacks:
- ❌ No per-user identities
- ❌ All posts from one account
- ❌ No true data portability
Path 2: Full PDS Deployment (Future)#
Goal: Run AC's own PDS with per-user identities
Details: See ATProto PDS Roadmap
Features:
- ✅ Per-user identities (
jeffrey.aesthetic.computer) - ✅ Custom lexicons (
computer.aesthetic.*) - ✅ True data portability
- ✅ Full federation
- ✅ Replace Auth0
Requirements:
- GCP Compute Engine e2-small (~$15-20/month)
- PostgreSQL database
- DNS configuration
- PDS deployment and maintenance
Development Workflow#
Testing Changes#
# 1. Make changes to tools
vim query-profile.mjs
# 2. Run tests
node test-all.mjs
# 3. Test specific tool
node query-profile.mjs aesthetic.computer
# 4. Check for lint errors
npm run test
Adding New Tools#
- Create new
.mjsfile in/at - Add to
package.jsonscripts section - Document in this MANIFEST
- Update README.md
- Test with
test-all.mjs
Updating Dependencies#
# Check for updates
npm outdated
# Update all to latest
npm update
# Update specific package
npm install @atproto/api@latest
Related Documentation#
- Main README:
/at/README.md- Full documentation - Quick Start:
/at/QUICKSTART.md- Get started quickly - PDS Roadmap:
/plans/atproto-pds-roadmap.md- Full PDS strategy - Data Architecture:
/plans/atproto-data-architecture.md- Data flow and storage
External Resources#
- ATProto Docs: https://atproto.com
- Bluesky API: https://docs.bsky.app
- ATProto GitHub: https://github.com/bluesky-social/atproto
- SDK Reference: https://github.com/bluesky-social/atproto/tree/main/packages/api
- Lexicon Specs: https://atproto.com/specs/lexicon
- PLC Directory: https://plc.directory
- Bluesky Settings: https://bsky.app/settings
Docker Integration#
Dockerfile Changes#
ATProto dependencies are now included in .devcontainer/Dockerfile:
# --- ATProto dependencies for /at directory ------------------------------------
# Note: These will be available globally for ATProto experiments
# See /at directory for ATProto/Bluesky exploration tools
RUN export PATH="/home/me/.fnm:${PATH}" && \
eval "$(/home/me/.fnm/fnm env --use-on-cd)" && \
npm i -g @atproto/api@^0.17.0 @atproto/xrpc@^0.6.5 @atproto/lexicon@^0.4.2 @atproto/identity@^0.4.3 @atproto/syntax@^0.3.1 dotenv@^16.4.5
This ensures all dependencies are available after container rebuilds.
Future Enhancements#
Planned Tools#
-
firehose-monitor.mjs- Monitor ATProto firehose in real-time -
create-lexicon.mjs- Generate custom lexicon schemas -
export-data.mjs- Export all data from a DID as CAR files -
migrate-to-pds.mjs- Migration tool for when AC runs own PDS -
analyze-network.mjs- Network analysis and metrics
Planned Features#
- Batch posting support
- Image optimization before upload
- Post scheduling
- Analytics and engagement tracking
- Custom lexicon validator
- PDS health monitoring
Changelog#
2025-10-06 - Initial Creation#
- ✅ Created
/atdirectory structure - ✅ Set up Node.js project with ATProto dependencies
- ✅ Implemented 6 core tools (query, post, explore, resolve, test)
- ✅ Documented intermediate integration path
- ✅ Added to main README and Dockerfile
- ✅ Tested against @aesthetic.computer Bluesky account
- ✅ Created comprehensive documentation
Status: Ready for experimentation and integration testing
Maintainer: Jeffrey Scudder (@jeffrey)
Last Updated: October 6, 2025
Version: 0.0.1 (Experimental)