gilesb 069010660d Show input content as thumbnails in activity/asset detail
- Display actual images/videos for inputs (not just hash links)
- Video auto-plays if detected, falls back to image
- Consistent display on both activity and asset detail pages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:52:52 +00:00
2026-01-07 19:54:11 +00:00
2026-01-07 17:16:05 +00:00
2026-01-07 12:04:58 +00:00

Art DAG L2 Server - ActivityPub

Ownership registry and ActivityPub federation for Art DAG.

What it does

  • Registry: Maintains owned assets with content hashes
  • Activities: Creates signed ownership claims (Create activities)
  • Federation: ActivityPub endpoints for follow/share
  • L1 Integration: Records completed L1 runs as owned assets
  • Authentication: User registration, login, JWT tokens

Setup

pip install -r requirements.txt

# Configure (optional - defaults shown)
export ARTDAG_DOMAIN=artdag.rose-ash.com
export ARTDAG_USER=giles
export ARTDAG_DATA=~/.artdag/l2
export ARTDAG_L1=http://localhost:8100

# Generate signing keys (required for federation)
python setup_keys.py

# Start server
python server.py

JWT Secret Configuration

The JWT secret is used to sign authentication tokens. Without a persistent secret, tokens are invalidated on server restart.

Generate a secret

# Generate a 64-character hex secret
openssl rand -hex 32
# Or with Python
python -c "import secrets; print(secrets.token_hex(32))"

Local development

export JWT_SECRET="your-generated-secret-here"
python server.py

Create a Docker secret:

# From a generated value
openssl rand -hex 32 | docker secret create jwt_secret -

# Or from a file
echo "your-secret-here" > jwt_secret.txt
docker secret create jwt_secret jwt_secret.txt
rm jwt_secret.txt

Reference in docker-compose.yml:

services:
  l2-server:
    secrets:
      - jwt_secret

secrets:
  jwt_secret:
    external: true

The server reads secrets from /run/secrets/jwt_secret automatically.

Key Setup

ActivityPub requires RSA keys for signing activities. Generate them:

# Local
python setup_keys.py

# Or with custom paths
python setup_keys.py --data-dir /data/l2 --user giles

# In Docker, exec into container or mount volume
docker exec -it <container> python setup_keys.py

Keys are stored in $ARTDAG_DATA/keys/:

  • {username}.pem - Private key (chmod 600, NEVER share)
  • {username}.pub - Public key (included in actor profile)

Important: Private keys are gitignored. Back them up securely. Losing them invalidates all your signatures.

API Endpoints

Server Info

Method Path Description
GET / Server info

ActivityPub

Method Path Description
GET /.well-known/webfinger?resource=acct:user@domain Actor discovery
GET /users/{username} Actor profile
GET /users/{username}/outbox Published activities
POST /users/{username}/inbox Receive activities
GET /users/{username}/followers Followers list
GET /objects/{content_hash} Get object by hash

Registry

Method Path Description
GET /registry Full registry
GET /registry/{name} Get asset by name
POST /registry Register new asset
POST /registry/record-run Record L1 run as owned asset

Example Usage

Register an asset

curl -X POST http://localhost:8200/registry \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-video",
    "content_hash": "abc123...",
    "asset_type": "video",
    "tags": ["art", "generated"]
  }'

Record an L1 run

curl -X POST http://localhost:8200/registry/record-run \
  -H "Content-Type: application/json" \
  -d '{
    "run_id": "uuid-from-l1",
    "output_name": "my-rendered-video"
  }'

Discover actor (WebFinger)

curl "http://localhost:8200/.well-known/webfinger?resource=acct:giles@artdag.rose-ash.com"

Get actor profile

curl -H "Accept: application/activity+json" http://localhost:8200/users/giles

Data Storage

Data stored in ~/.artdag/l2/:

  • registry.json - Asset registry
  • activities.json - Signed activities
  • actor.json - Actor profile
  • followers.json - Followers list

Architecture

L2 Server (port 8200)
    │
    ├── POST /registry → Register asset → Create activity → Sign
    │
    ├── POST /registry/record-run → Fetch L1 run → Register output
    │       │
    │       └── GET L1_SERVER/runs/{id}
    │
    ├── GET /users/{user}/outbox → Return signed activities
    │
    └── POST /users/{user}/inbox → Receive Follow requests
Description
No description provided
Readme 2.2 MiB
Languages
Python 91.3%
HTML 8.4%
Dockerfile 0.2%
Shell 0.1%