2026-01-09 18:21:36 +00:00
2026-01-09 02:58:56 +00:00
2026-01-09 11:16:49 +00:00
2026-01-08 00:59:20 +00:00

Art Celery

L1 rendering server for the Art DAG system. Manages distributed rendering jobs via Celery workers.

Dependencies

  • artdag (GitHub): Core DAG execution engine
  • artdag-effects (rose-ash): Effect implementations
  • Redis: Message broker, result backend, and run persistence

Setup

# Install Redis
sudo apt install redis-server

# Install Python dependencies
pip install -r requirements.txt

# Start a worker
celery -A celery_app worker --loglevel=info

# Start the L1 server
python server.py

Web UI

The server provides a web interface at the root URL:

Path Description
/ Home page with server info
/runs View and manage rendering runs
/run/{id} Run detail page
/recipes Browse and run available recipes
/recipe/{id} Recipe detail page
/media Browse cached media files
/auth Receive auth token from L2
/auth/revoke Revoke a token (called by L2 on logout)
/logout Log out
/download/client Download CLI client

Authentication

L1 servers authenticate users via L2 (the ActivityPub registry). No shared secrets are required.

Configuration

export L1_PUBLIC_URL=https://celery-artdag.rose-ash.com

How it works

  1. User clicks "Attach" on L2's Renderers page
  2. L2 creates a scoped token bound to this specific L1
  3. User is redirected to L1's /auth?auth_token=...
  4. L1 calls L2's /auth/verify to validate the token
  5. L2 checks: token valid, not revoked, scope matches this L1
  6. L1 sets a local cookie and records the token

Token revocation

When a user logs out of L2, L2 calls /auth/revoke on all attached L1s. L1 maintains a Redis-based revocation list:

  • Revoked tokens stored in Redis with 30-day expiry
  • Every authenticated request checks the revocation list
  • Revoked tokens are immediately rejected

Security

  • Scoped tokens: Tokens are bound to a specific L1. A stolen token can't be used on other L1 servers.
  • L2 verification: L1 verifies every token with L2, which checks its revocation table.
  • No shared secrets: L1 doesn't need L2's JWT secret.

API

Interactive docs: http://localhost:8100/docs

Endpoints

Method Path Description
GET / Server info
POST /runs Start a rendering run
GET /runs List all runs
GET /runs/{run_id} Get run status
DELETE /runs/{run_id} Delete a run
GET /cache List cached content hashes
GET /cache/{hash} Download cached content
DELETE /cache/{hash} Delete cached content
POST /cache/import?path= Import local file to cache
POST /cache/upload Upload file to cache
GET /assets List known assets
POST /configs/upload Upload a config YAML
GET /configs List configs
GET /configs/{id} Get config details
DELETE /configs/{id} Delete a config
POST /configs/{id}/run Run a config

Configs

Configs are YAML files that define reusable DAG pipelines. They can have:

  • Fixed inputs: Assets with pre-defined content hashes
  • Variable inputs: Placeholders filled at run time

Example config:

name: my-effect
version: "1.0"
description: "Apply effect to user image"

registry:
  effects:
    dog:
      hash: "abc123..."

dag:
  nodes:
    - id: user_image
      type: SOURCE
      config:
        input: true        # Variable input
        name: "input_image"

    - id: apply_dog
      type: EFFECT
      config:
        effect: dog
      inputs:
        - user_image

  output: apply_dog

Start a run

curl -X POST http://localhost:8100/runs \
  -H "Content-Type: application/json" \
  -d '{"recipe": "dog", "inputs": ["33268b6e..."], "output_name": "my-output"}'

Check run status

curl http://localhost:8100/runs/{run_id}

Delete a run

curl -X DELETE http://localhost:8100/runs/{run_id} \
  -H "Authorization: Bearer <token>"

Note: Failed runs can always be deleted. Completed runs can only be deleted if their outputs haven't been published to L2.

Delete cached content

curl -X DELETE http://localhost:8100/cache/{hash} \
  -H "Authorization: Bearer <token>"

Note: Items that are inputs/outputs of runs, or published to L2, cannot be deleted.

Storage

  • Cache: ~/.artdag/cache/ (content-addressed files)
  • Runs: Redis db 5, keys artdag:run:* (persists across restarts)

CLI Usage

# Render cat through dog effect
python render.py dog cat --sync

# Render cat through identity effect
python render.py identity cat --sync

# Submit async (don't wait)
python render.py dog cat

Architecture

server.py (L1 Server - FastAPI)
    │
    ├── POST /runs → Submit job
    │       │
    │       ▼
    │   celery_app.py (Celery broker)
    │       │
    │       ▼
    │   tasks.py (render_effect task)
    │       │
    │       ├── artdag (GitHub) - DAG execution
    │       └── artdag-effects (rose-ash) - Effects
    │               │
    │               ▼
    │           Output + Provenance
    │
    └── GET /cache/{hash} → Retrieve output

Provenance

Every render produces a provenance record:

{
  "task_id": "celery-task-uuid",
  "rendered_at": "2026-01-07T...",
  "rendered_by": "@giles@artdag.rose-ash.com",
  "output": {"name": "...", "content_hash": "..."},
  "inputs": [...],
  "effects": [...],
  "infrastructure": {
    "software": {"name": "infra:artdag", "content_hash": "..."},
    "hardware": {"name": "infra:giles-hp", "content_hash": "..."}
  }
}
Description
No description provided
Readme 8.4 MiB
Languages
Python 87.9%
HTML 5.9%
Common Lisp 5.7%
Shell 0.5%