# 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 ```bash 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 DATABASE_URL=postgresql://artdag:artdag@localhost:5432/artdag # 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 ```bash # Generate a 64-character hex secret openssl rand -hex 32 # Or with Python python -c "import secrets; print(secrets.token_hex(32))" ``` ### Local development ```bash export JWT_SECRET="your-generated-secret-here" python server.py ``` ### Docker Swarm (recommended for production) Create a Docker secret: ```bash # 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: ```yaml 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: ```bash # 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 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. ## Client Commands ### Upload Media Register a media asset (image, video, audio) with a content hash: ```bash curl -X POST http://localhost:8200/assets \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "name": "my-video", "content_hash": "abc123...", "asset_type": "video", "tags": ["art", "generated"] }' ``` ### Upload Recipe Record an L1 run as an owned asset. This fetches the run details from the L1 server and registers the output: ```bash curl -X POST http://localhost:8200/assets/record-run \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "run_id": "uuid-from-l1", "l1_server": "https://celery-artdag.rose-ash.com", "output_name": "my-rendered-video" }' ``` ## API Endpoints ### Server Info | Method | Path | Description | |--------|------|-------------| | GET | `/` | Home page with stats | ### Assets | Method | Path | Description | |--------|------|-------------| | GET | `/assets` | List all assets | | GET | `/assets/{name}` | Get asset by name | | POST | `/assets` | Upload media - register new asset | | POST | `/assets/record-run` | Upload recipe - record L1 run | ### 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 | | GET | `/activities/{index}` | Get activity by index | ### Authentication | Method | Path | Description | |--------|------|-------------| | POST | `/auth/register` | Register new user | | POST | `/auth/login` | Login, get JWT token | | GET | `/auth/me` | Get current user | ## Data Storage Data stored in PostgreSQL: - `users` - Registered users - `assets` - Asset registry - `activities` - Signed activities - `followers` - Followers list RSA keys stored in `$ARTDAG_DATA/keys/` (files, not database). ## Architecture ``` L2 Server (port 8200) │ ├── POST /assets (upload media) → Register asset → Create activity → Sign │ ├── POST /assets/record-run (upload recipe) → Fetch L1 run → Register output │ │ │ └── GET L1_SERVER/runs/{id} │ ├── GET /users/{user}/outbox → Return signed activities │ └── POST /users/{user}/inbox → Receive Follow requests ```