2026-01-25 15:49:15 +09:00
2026-01-18 20:43:18 +09:00
2026-01-25 15:49:15 +09:00
2026-01-25 15:49:15 +09:00
2026-01-25 15:49:15 +09:00
2026-01-20 17:32:27 +09:00
2026-01-20 23:44:24 +09:00
2026-01-20 17:32:27 +09:00
2026-01-20 19:14:16 +09:00
2026-01-20 21:32:46 +09:00
2026-01-18 18:08:53 +09:00
2026-01-18 18:08:53 +09:00

ailog

atproto blog cli

https://git.syui.ai/ai/log

$ git clone https://git.syui.ai/ai/log
$ cd log
$ cat public/config.json
$ npm run dev

oauth

Use ATProto OAuth to login from the browser and create, edit, or delete posts.

Setup

1. Edit client-metadata.json

Modify public/client-metadata.json with your own domain:

{
  "client_id": "https://example.com/client-metadata.json",
  "client_name": "example.com",
  "client_uri": "https://example.com",
  "redirect_uris": ["https://example.com/"],
  "scope": "atproto transition:generic",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "application_type": "web",
  "token_endpoint_auth_method": "none",
  "dpop_bound_access_tokens": true
}

Required changes:

Field Description
client_id URL of this file. Must be https://yourdomain.com/client-metadata.json
client_name App name (shown on auth screen)
client_uri Your site URL
redirect_uris Redirect URL after OAuth. Use your site's root URL

2. Deploy the file

client-metadata.json must be publicly accessible at:

https://yourdomain.com/client-metadata.json

The ATProto PDS fetches this file during authentication, so it must be accessible via public URL.

3. Local development

No configuration needed for local development (localhost/127.0.0.1). The code automatically uses ATProto's loopback client ID:

http://localhost?redirect_uri=http://127.0.0.1:5173/&scope=atproto%20transition%3Ageneric

4. Network configuration

To support multiple PDS servers, define networks in public/network.json:

{
  "bsky.social": {
    "bsky": "https://bsky.social",
    "plc": "https://plc.directory"
  },
  "syu.is": {
    "bsky": "https://bsky.syu.is",
    "plc": "https://plc.syu.is",
    "web": "https://syu.is"
  }
}

The appropriate PDS is automatically selected based on the handle's domain.

Troubleshooting

  • Auth error: Verify client_id matches the actual file URL
  • Redirect error: Verify redirect_uris matches your site URL
  • CORS error: Verify client-metadata.json is served with correct Content-Type

cli

$ cargo build
$ ./target/debug/ailog

login (l)

login to atproto pds.

$ ailog login <handle> -p <password> [-s <server>]
$ ailog l user.bsky.social -p mypassword
$ ailog l user.syu.is -p mypassword -s syu.is

post (p)

post a record to collection.

$ ailog post <file> -c <collection> [-r <rkey>]
$ ailog p ./post.json -c ai.syui.log.post
$ ailog p ./post.json -c ai.syui.log.post -r 3abc123

get (g)

get records from collection.

$ ailog get -c <collection> [-l <limit>]
$ ailog g -c ai.syui.log.post
$ ailog g -c ai.syui.log.post -l 20

delete (d)

delete a record from collection.

$ ailog delete -c <collection> -r <rkey>
$ ailog d -c ai.syui.log.post -r 3abc123

sync (s)

sync pds data to local content directory.

$ ailog sync [-o <output>]
$ ailog s
$ ailog s -o ./public/content
$ ailog s -c ai.syui.log.chat

lexicon

update lexicon schema.

$ ailog lexicon <file>
$ ailog lexicon ./lexicons/ai.syui.log.post.json
$ ailog did syui.ai
did:plc:uqzpqmrjnptsxezjx4xuh2mn
_lexicon.log.syui.ai  txt  "did=did:plc:uqzpqmrjnptsxezjx4xuh2mn"

gen

generate lexicon code from atproto lexicon json files.

$ ailog gen [-i <input>] [-o <output>]
$ ailog gen
$ ailog gen -i ./repos/atproto/lexicons -o ./src/lexicons

lang

translate content files using lms.

$ ailog lang <input> [-f <from>] [-t <to>]
$ ailog lang ./post.json
$ ailog lang ./public/content -f ja -t en

requires .env:

TRANSLATE_URL=http://127.0.0.1:1234/v1
TRANSLATE_MODEL=plamo-2-translate

Lexicon Validation (Browser)

AT-Browser has a "Validate" button on record detail pages to validate records against their lexicon schema.

How it works

NSID: app.bsky.actor.profile
        ↓
1. Parse NSID → authority: actor.bsky.app
        ↓
2. DNS TXT lookup: _lexicon.actor.bsky.app
   → did=did:plc:xxx
        ↓
3. Resolve DID → PDS endpoint
        ↓
4. Fetch lexicon from PDS:
   com.atproto.repo.getRecord
   - repo: did:plc:xxx
   - collection: com.atproto.lexicon.schema
   - rkey: app.bsky.actor.profile
        ↓
5. Validate record with @atproto/lexicon

DNS TXT Record Setup

To publish your own lexicon, set a DNS TXT record:

_lexicon.log.syui.ai  TXT  "did=did:plc:uqzpqmrjnptsxezjx4xuh2mn"

Then create the lexicon record in your repo under com.atproto.lexicon.schema collection.

Browser-compatible DNS lookup

Uses Cloudflare DNS-over-HTTPS (DoH) for browser compatibility:

https://mozilla.cloudflare-dns.com/dns-query?name=_lexicon.actor.bsky.app&type=TXT

Note: com.atproto.lexicon.resolveLexicon

ATProto spec defines com.atproto.lexicon.resolveLexicon endpoint, but it's not yet implemented on any PDS (bsky.social, syu.is, etc.):

$ curl "https://bsky.social/xrpc/com.atproto.lexicon.resolveLexicon?nsid=app.bsky.actor.profile"
{"error":"XRPCNotSupported","message":"XRPCNotSupported"}

The current implementation uses the DNS-based approach instead, which works today.

Reference

chat

Chat with AI bot and save conversations to ATProto.

Setup

  1. Login as user and bot:
# User login
$ ailog login user.syu.is -p <password> -s syu.is

# Bot login
$ ailog login ai.syu.is -p <password> -s syu.is --bot
  1. Configure LLM endpoint in .env:
CHAT_URL=http://127.0.0.1:1234/v1
CHAT_MODEL=gpt-oss-20b
  1. (Optional) Set character/system prompt:
# Direct prompt
CHAT_SYSTEM="You are ai, a friendly AI assistant."

# Or load from file
CHAT_SYSTEM_FILE=./character.txt

Usage

# Start a new conversation
$ ailog chat --new "hello"

# Continue the conversation
$ ailog chat "how are you?"

# Interactive mode (new session)
$ ailog chat --new

# Interactive mode (continue)
$ ailog chat

Data Storage

Messages are saved locally to public/content/{did}/ai.syui.log.chat/:

public/content/
├── did:plc:xxx/              # User's messages
│   └── ai.syui.log.chat/
│       ├── index.json
│       └── {rkey}.json
└── did:plc:yyy/              # Bot's messages
    └── ai.syui.log.chat/
        ├── index.json
        └── {rkey}.json

Sync & Push

# Sync bot data from PDS to local
$ ailog sync --bot

# Push local chat to PDS
$ ailog push -c ai.syui.log.chat --bot

Web Display

View chat threads at /@{handle}/at/chat:

  • /@user.syu.is/at/chat - Thread list (conversations started by user)
  • /@user.syu.is/at/chat/{rkey} - Full conversation thread

Record Schema

{
  "$type": "ai.syui.log.chat",
  "content": "message text",
  "author": "did:plc:xxx",
  "createdAt": "2025-01-01T00:00:00.000Z",
  "root": "at://did:plc:xxx/ai.syui.log.chat/{rkey}",
  "parent": "at://did:plc:yyy/ai.syui.log.chat/{rkey}"
}
  • root: First message URI in the thread (empty for conversation start)
  • parent: Previous message URI in the thread

Claude Code Integration (MCP)

Use Claude Code to chat and automatically save conversations.

1. Setup MCP server:

# Add MCP server
$ claude mcp add ailog /path/to/ailog mcp-serve

# Or with full path
$ claude mcp add ailog ~/ai/log/target/release/ailog mcp-serve

# Verify
$ claude mcp list

Or manually edit ~/.claude.json:

{
  "mcpServers": {
    "ailog": {
      "command": "/path/to/ailog",
      "args": ["mcp-serve"]
    }
  }
}

2. Chat with Claude:

$ cd ~/ai/log
$ claude
> こんにちは

# Claude:
# 1. get_character でキャラクター設定取得
# 2. キャラクター(アイ)として応答
# 3. chat_save で会話を自動保存

MCP Tools:

  • get_character - Get AI character settings from .env
  • chat_save - Save conversation exchange
  • chat_list - List recent messages
  • chat_new - Start new thread
Description
No description provided
https://syui.ai
Readme 156 MiB
Languages
TypeScript 71.5%
CSS 28.2%
HTML 0.3%