# ailog ATProto-based blog platform built on at-browser. ## Concept **Data lives in ATProto, not on this server.** This is not a traditional blog generator. It's a **viewer (client)** for ATProto records. ``` Traditional blog: Server DB ← article data ← user ATProto blog: User's PDS ← article data (ai.syui.log.post) ↓ at-browser (this site) → displays records ``` ## Architecture ``` ┌─────────────────────────────────────────┐ │ at-browser │ │ (ATProto record viewer/editor) │ ├─────────────────────────────────────────┤ │ │ │ / → admin (config.json) │ │ /@alice → user page │ │ /@bob.bsky → user page │ │ │ └─────────────────────────────────────────┘ ``` ## Roles | Role | Path | Data Source | |------|------|-------------| | **admin** | `/` (root) | local + remote | | **user** | `/@handle` | remote only | ### Admin (Site Owner) - Defined in `config.json` - Has root (`/`) access - Can reference **local files** (static assets, custom styles) - Can reference **remote** (ATProto records) ### User (Any ATProto User) - Accessed via `/@handle` path - **Remote only** (ATProto records from their PDS) - No registration required - Anyone with an ATProto account can be displayed ## Features ### 1. at-browser (Core) - Search by handle/DID - Browse PDS collections - Navigate ATProto records ### 2. ai.syui.log.post View - Markdown rendering - Syntax highlighting - Blog-style display ### 3. OAuth - Login with ATProto - Post to ai.syui.log.post collection ## Use Cases ### Personal Blog ```json // config.json { "did": "did:plc:xxxxx", "handle": "syui.syui.ai" } ``` - Deploy to `syui.ai` - Root shows your profile + posts - You are the admin (local + remote) - Others can view via `/@handle` ### Blog Service ```json // config.json { "admin": "service.example.com", "handle": null } ``` - Deploy to `blog.example.com` - Root shows landing/search - All users via `/@handle` (remote only) - Platform for any ATProto user ## Data Flow ``` ┌──────────────┐ ┌──────────────┐ │ User's PDS │────→│ at-browser │ │ (ATProto) │←────│ (this site) │ └──────────────┘ └──────────────┘ ↑ │ │ ↓ ai.syui.log.post ┌──────────┐ collection │ Display │ │ - Profile│ │ - Posts │ └──────────┘ ``` ## Local = Remote (Same Format) **Critical design principle: local files use the exact same format as ATProto API responses.** This allows the same code to handle both data sources. ### Remote (ATProto API) ```bash curl "https://syu.is/xrpc/com.atproto.repo.listRecords?repo=did:plc:xxx&collection=ai.syui.log.post" ``` ```json { "records": [ { "uri": "at://did:plc:xxx/ai.syui.log.post/3xxx", "cid": "bafyrei...", "value": { "title": "Hello World", "content": "# Hello\n\nThis is my post.", "createdAt": "2025-01-01T00:00:00Z" } } ] } ``` ### Local (Static File) ``` content/ └── did:plc:xxx/ ├── describe.json # describeRepo (special) ├── app.bsky.actor.profile/ │ └── self.json # {collection}/{rkey}.json └── ai.syui.log.post/ └── 3xxx.json # {collection}/{rkey}.json ``` ```json // content/did:plc:xxx/ai.syui.log.post/3xxx.json { "uri": "at://did:plc:xxx/ai.syui.log.post/3xxx", "cid": "bafyrei...", "value": { "title": "Hello World", "content": "# Hello\n\nThis is my post.", "createdAt": "2025-01-01T00:00:00Z" } } ``` ### ATProto API Reference | API | Path | Description | |-----|------|-------------| | getRecord | `/xrpc/com.atproto.repo.getRecord` | Get single record | | listRecords | `/xrpc/com.atproto.repo.listRecords` | List records in collection | | describeRepo | `/xrpc/com.atproto.repo.describeRepo` | Get repo info + collections list | See: [com.atproto.repo.describeRepo](https://docs.bsky.app/docs/api/com-atproto-repo-describe-repo) ### Resolution Strategy ``` at-browser │ ├── admin (config.json user) │ ├── 1. Check local: /content/{did}/{collection}/{rkey}.json │ └── 2. Fallback to remote: PDS API │ └── user (/@handle) └── remote only: PDS API ``` ### Why Same Format? - **One codebase**: No branching logic for local vs remote - **Easy testing**: Copy API response to local file - **Offline support**: Admin can work with local files - **Migration**: Local → Remote (just POST to PDS) ## Config ### config.json ```json { "did": "did:plc:xxxxx", "handle": "syui.syui.ai", "pds": "syu.is", "collection": "ai.syui.log.post" } ``` ## Tech Stack - **CLI**: Rust (ailog) - **Frontend**: Vite + TypeScript - **ATProto**: @atproto/api - **OAuth**: @atproto/oauth-client-browser - **Markdown**: marked + highlight.js ## CLI (ailog) ### Install ```bash cargo build --release cp target/release/ailog ~/.local/bin/ ``` ### Commands ```bash # Login to ATProto PDS ailog login -p [-s ] # Post a record ailog post -c [-r ] # Get records from collection ailog get -c [-l ] # Delete a record ailog delete -c -r # Sync PDS data to local content directory ailog sync [-o ] # Generate lexicon Rust code from ATProto lexicons ailog gen [-i ] [-o ] ``` ### Example ```bash # Login ailog login syui.syui.ai -p "app-password" -s syu.is # Post echo '{"title":"Hello","content":"World","createdAt":"2025-01-01T00:00:00Z"}' > post.json ailog post post.json -c ai.syui.log.post # Sync to local ailog sync -o content ``` ### Project Structure ``` src/ ├── main.rs ├── commands/ │ ├── mod.rs │ ├── auth.rs # login, refresh session │ ├── token.rs # token management │ ├── post.rs # post, get, delete, sync │ └── gen.rs # lexicon code generation └── lexicons/ └── mod.rs # auto-generated from ATProto lexicons ``` ### Lexicon Generation Generate Rust endpoint definitions from ATProto lexicon JSON files: ```bash # Clone atproto repo (if not exists) git clone https://github.com/bluesky-social/atproto repos/atproto # Generate lexicons ailog gen -i ./repos/atproto/lexicons -o ./src/lexicons # Rebuild cargo build ``` ## Collection Schema ### ai.syui.log.post ```json { "title": "Post Title", "content": "Markdown content...", "createdAt": "2025-01-01T00:00:00Z" } ``` ## Assets ### PNG to SVG Conversion (Vector Trace) Convert PNG images to true vector SVG using vtracer (Rust): ```bash # Install vtracer cargo install vtracer # Convert PNG to SVG (color mode) vtracer --input input.png --output output.svg --colormode color # Convert PNG to SVG (black and white) vtracer --input input.png --output output.svg ``` **Options:** - `--colormode color` : Preserve colors (recommended for icons) - `--colormode binary` : Black and white only - `--filter_speckle 4` : Remove small artifacts - `--corner_threshold 60` : Adjust corner detection **Alternative tools:** - potrace: `potrace input.pbm -s -o output.svg` (B&W only, requires PBM input) - Inkscape CLI: `inkscape input.png --export-type=svg` (embeds image, no trace) **Note:** Inkscape's CLI `--export-type=svg` only embeds the PNG, it does not trace. For true vectorization, use vtracer or potrace. ## License MIT