Commit Graph

44 Commits

Author SHA1 Message Date
gilesb
3ed4fe89ed Add content-addressable runs and activities
- compute_run_id() for deterministic run identification
- /assets/by-run-id/{run_id} endpoint for L1 recovery
- Store run_id in provenance when recording runs
- Activities now use content hash as ID instead of UUID
- URLs: /activities/{content_hash} instead of /activities/1

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 11:04:59 +00:00
gilesb
0f01d8e12c Add 404 page and Web UI section to README
- Add styled 404 page for HTML requests (returns JSON for API calls)
- Add Web UI section to README documenting available routes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 10:42:44 +00:00
gilesb
0a0dd278fe Add Tailwind typography plugin for prose styling
The README on the home page uses prose classes which require the
typography plugin to render headers and other markdown elements.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 10:30:32 +00:00
gilesb
29b838fdda Fix uvicorn workers - use import string
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:41:29 +00:00
gilesb
32435fe291 Add timing to L1 request for debugging
Log how long requests.get() actually takes to identify network delays.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:38:27 +00:00
gilesb
3756f5e630 Add uvicorn workers for better concurrency
Run with 4 workers to handle concurrent requests without blocking.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:36:16 +00:00
gilesb
43a9a1cd64 Store full provenance on IPFS with provenance_cid
Provenance records are now stored on IPFS before the DB transaction.
The provenance CID is included in both:
- The provenance stored in the asset record
- The ActivityPub activity object

This enables:
- Immutable, content-addressed provenance
- Bitcoin timestamping of provenance for cross-L2 dispute resolution
- Verifiable chain of custody: inputs → recipe → output

Provenance structure on IPFS:
{
  "inputs": [...],
  "output": {"content_hash": "...", "ipfs_cid": "..."},
  "recipe": {...},
  "recipe_cid": "Qm...",
  "provenance_cid": "Qm...",  // self-reference for verification
  ...
}

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 02:31:03 +00:00
gilesb
91d8093a1b Improve L1 fetch error handling with better diagnostics
- Handle 404 explicitly with clear "not found" message
- Log actual response body when JSON parsing fails
- Include status code and body preview in error messages

This helps diagnose issues like empty responses or HTML error pages from L1.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 02:00:27 +00:00
gilesb
449ed0c100 Add comprehensive logging to publishing endpoints
Added logging throughout record_run, publish_cache, and _register_asset_impl:
- Log start of each operation with key parameters
- Log L1 fetch operations and their results
- Log IPFS pin operations
- Log DB transaction start and completion
- Log errors with context
- Log successful completion

This makes it easier to diagnose timeout and other issues.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 01:54:17 +00:00
gilesb
9ef45e295b Make all IPFS and L1 operations non-blocking
Wrap synchronous requests.get() and ipfs_client calls in
asyncio.to_thread() to prevent blocking the FastAPI event loop.
This fixes web UI slowness during publishing operations.

- record_run: L1 fetches and IPFS operations now async
- _register_asset_impl: IPFS pin now async
- publish_cache: IPFS pin now async
- get_anchor_tree: IPFS get_bytes now async
- verify_activity_anchor: IPFS get_bytes now async

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 01:42:15 +00:00
gilesb
7ead2026ef Add Bitcoin anchoring via OpenTimestamps
Implements merkle tree anchoring for ActivityPub activities with
provable Bitcoin timestamps. Activities are batched into merkle trees,
submitted to OpenTimestamps (free), and proofs stored on IPFS.

Key features:
- Merkle tree construction with membership proofs
- OpenTimestamps integration for Bitcoin anchoring
- JSONL backup file on persistent volume (survives DB wipes)
- API endpoints for creating/verifying anchors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 01:22:39 +00:00
gilesb
2267096271 Add format_date helper to handle datetime objects in templates
The db now returns datetime objects instead of strings in some cases.
Added format_date() helper function that handles both datetime and string
values, and replaced all [:10] date slicing with calls to this helper.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 01:20:32 +00:00
gilesb
647c564c47 Implement atomic publishing with IPFS and DB transactions
All publishing operations now use three-phase atomic approach:
1. Phase 1: Preparation - validate inputs, gather IPFS CIDs
2. Phase 2: IPFS operations - pin all content before any DB changes
3. Phase 3: DB transaction - all-or-nothing database commits

Changes:

ipfs_client.py:
- Add IPFSError exception class
- Add add_bytes() to store content on IPFS
- Add add_json() to store JSON documents on IPFS
- Add pin_or_raise() for synchronous pinning with error handling

db.py:
- Add transaction() context manager for atomic DB operations
- Add create_asset_tx() for transactional asset creation
- Add create_activity_tx() for transactional activity creation
- Add get_asset_by_hash_tx() for lookup within transactions
- Add asset_exists_by_name_tx() for existence check within transactions

server.py:
- Rewrite record_run:
  - Check L2 first for inputs, fall back to L1
  - Store recipe JSON on IPFS with CID in provenance
  - Auto-register input assets if not already on L2
  - All operations atomic
- Rewrite publish_cache:
  - IPFS CID now required
  - Synchronous pinning before DB commit
  - Transaction for asset + activity
- Rewrite _register_asset_impl:
  - IPFS CID now required
  - Synchronous pinning before DB commit
  - Transaction for asset + activity

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 00:59:12 +00:00
gilesb
a0ed1ae5ae Make IPFS pinning non-blocking (fire-and-forget)
IPFS pinning can take a long time if content needs to be fetched
from the network. Changed all pin operations to run in background
threads so they don't block the HTTP response.

This fixes the 30s timeout issue when publishing assets.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 00:26:32 +00:00
gilesb
a7dfdc8a39 Fix asset_type detection when recording runs from L1
Fetch media_type and ipfs_cid from L1's cache endpoint instead of
hardcoding asset_type to "video". This fixes images being incorrectly
displayed as videos.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 00:22:58 +00:00
gilesb
e69b72cbe1 Add ipfs_cid support to asset update endpoint
Allow updating asset's IPFS CID via PATCH endpoint.
Pins content on IPFS when CID is provided.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 23:21:20 +00:00
gilesb
ba7dc3b89c Fix NoneType error when asset provenance is null
Use 'or {}' pattern to handle both missing keys and explicit None
values for origin, provenance, and metadata fields.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 23:02:15 +00:00
gilesb
fd239d99f9 Fix f-string syntax error in IPFS display
Build ipfs_html separately to avoid nested f-string issues.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 19:18:41 +00:00
gilesb
8ae354d503 Add IPFS info display to L2 asset detail page
- Add IPFS_GATEWAY_URL config
- Show IPFS CID on asset detail page
- Add gateway links (local, ipfs.io, dweb.link)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 18:57:00 +00:00
gilesb
96631e1e4c Add IPFS node to L2 for federated content storage
- Add IPFS container to docker-compose
- Add ipfshttpclient dependency
- Add ipfs_client.py module for IPFS operations
- Add ipfs_cid field to Asset model and database schema
- Pin content on L2 IPFS when assets are published/registered

L2 now stores content on its own IPFS node, enabling
federation - content remains available even if L1 goes down.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 18:21:33 +00:00
gilesb
82cb1cf711 Fix logout to clear both legacy and shared domain cookies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 17:38:14 +00:00
gilesb
1d463352a7 Add configurable cookie domain for cross-subdomain auth sharing
- Add COOKIE_DOMAIN env var (e.g., ".rose-ash.com")
- Auto-derive from ARTDAG_DOMAIN if not set (strips first subdomain)
- Set domain on auth cookies for sharing across L1/L2 subdomains
- Add secure=True for cross-subdomain cookies

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 17:26:42 +00:00
gilesb
5eb525d107 Add l2_server claim to JWT tokens for L1 verification
L1 needs to know which L2 server issued the token so it can verify
the token with the correct server. Now tokens include:
- l2_server: The L2 server URL (e.g., https://artdag.rose-ash.com)
- username: Also include username for compatibility (in addition to sub)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 17:10:33 +00:00
gilesb
9c10de7534 Fix Swarm compatibility, add legacy /registry/record-run route
- Fix depends_on syntax for Docker Swarm (simple list, not extended)
- Add /registry/record-run as legacy alias for /assets/record-run

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 00:33:59 +00:00
gilesb
a6e83c72bd Migrate to PostgreSQL database, consolidate routes, improve home page
- Add PostgreSQL with asyncpg for persistent storage
- Create db.py module with async database operations
- Create migrate.py script to migrate JSON data to PostgreSQL
- Update docker-compose.yml with PostgreSQL service
- Home page now shows README with styled headings
- Remove /ui prefix routes, use content negotiation on main routes
- Add /activities/{idx} as canonical route (with /activity redirect)
- Update /assets/{name} to support HTML and JSON responses
- Convert auth.py to use async database operations
- RSA keys still stored as files in $ARTDAG_DATA/keys/

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 00:22:21 +00:00
gilesb
cb848aacbe Clean URLs, rename registry to assets, home page with counts
- Home page shows asset/activity/user counts with links (not redirect)
- Rename /registry to /assets everywhere
- Clean auth routes: /login, /logout, /register
- Update all navigation links to clean URLs
- Remove /ui prefix from main links

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 23:21:22 +00:00
gilesb
11fa01a864 Remove /ui prefix - clean URLs with content negotiation
- /registry, /activities, /users → HTML (browsers) or JSON (APIs)
- /asset/{name}, /activity/{index}, /users/{username} → same
- Infinite scroll on all list pages via HTMX
- API pagination: ?page=1&limit=20 with has_more
- All internal links updated to use clean URLs

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 22:09:38 +00:00
gilesb
f1aea5a5f3 Add content negotiation for /users and /objects endpoints
- /users/{username}: Redirects to /ui/user/{username} for browsers (Accept: text/html)
- /objects/{hash}: Redirects to /ui/asset/{name} for browsers
- APIs still get JSON (application/activity+json) as before

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 22:01:22 +00:00
gilesb
6c59ac0cbc Fix activity detail to show provenance/inputs
- Include provenance in activity object_data when creating activities
- Add fallback: look up asset from registry if activity lacks provenance
- Existing activities now show inputs by looking up the asset

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:59:38 +00:00
gilesb
151d33cb72 Add explicit View button to activities list 2026-01-07 21:54:59 +00:00
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
gilesb
0602195ed0 Add activity detail page with full content display
- Activities list now links to detail page
- Activity detail shows:
  - Video/image content inline (from L1)
  - Download button
  - Actor, description, origin
  - Full provenance (effect link, inputs, infrastructure)
  - ActivityPub URLs
- Updated activities table to show actor column

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:43:02 +00:00
gilesb
edc216c81f Store and display full provenance including effect_url
- record-run now stores effect_url, effects_commit, infrastructure in provenance
- Asset detail uses stored effect_url (with fallback for older records)
- Shows effects commit hash under effect button
- Shows infrastructure info (software/hardware) if available

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:29:40 +00:00
gilesb
c4b861c553 Enhance L2 asset detail with content display and provenance
- Add L1_PUBLIC_URL and EFFECTS_REPO_URL config
- Display images/videos directly from L1 cache
- Show provenance section for rendered outputs:
  - Effect name with link to source code
  - Input content hashes (linked)
  - L1 run ID (linked)
  - Render timestamp
- Download button for content

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:26:14 +00:00
gilesb
a0cdf31c36 Add asset detail and user detail UI pages
- /ui/asset/{name}: Shows full asset info (owner, hash, origin, tags, description, ActivityPub URL)
- /ui/user/{username}: Shows user profile with their published assets and activity stats
- Updated users list to link to user 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:18:52 +00:00
gilesb
231cd1653f Update UI to use Tailwind CSS with dark theme
- Replace custom CSS with Tailwind CSS via CDN
- Dark theme matching L1 server styling
- Responsive layouts for all pages
- Updated: home, login/register, registry, activities, users pages
- Modern form styling and table layouts

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 20:44:19 +00:00
gilesb
4155427f03 feat: multi-actor ActivityPub support
Each registered user now has their own ActivityPub actor:
- Generate RSA keys per user on registration
- Webfinger resolves any registered user (@user@domain)
- Actor endpoints work for any registered user
- Each user has their own outbox (filtered activities)
- Activities signed with the publishing user's keys
- Objects attributed to the asset owner

Removed:
- ARTDAG_USER config (no longer single-actor)
- L1_SERVER config (comes with each request)

Added:
- /ui/users page listing all registered users
- user_exists() helper function

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 19:54:11 +00:00
gilesb
58a125de1a feat: L1 server URL sent with publish request (many-to-many support)
- Add l1_server field to RecordRunRequest
- L2 fetches run data from the specified L1 URL instead of hardcoded config
- Store l1_server in provenance and metadata
- Remove ARTDAG_L1 config requirement from L2
- Update docker-stack.yml comments

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 19:40:53 +00:00
gilesb
0edea7b91b feat: add publish-cache and update asset endpoints
- Add POST /registry/publish-cache for publishing cache items with metadata
- Requires origin (self or external URL) for publishing
- Add PATCH /registry/{name} for updating existing assets
- Update activities now created when assets are modified
- Ownership check ensures only asset owner can update
- Origin info included in ActivityPub objects (generator/source)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 19:04:52 +00:00
gilesb
9a0e77a852 feat: require auth for registry endpoints and track asset ownership
- Add authentication to /registry endpoint
- Add authentication to /registry/record-run endpoint
- Extract register logic to _register_asset_impl helper
- Store owner username in registered assets
- Use authenticated user for ActivityPub actor ID in activities

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 17:25:59 +00:00
gilesb
d83510f24b feat: add HTMX web UI with login/register forms
- Home page showing README and stats
- Login/register forms with HTMX
- Registry and activities pages
- Cookie-based auth for web UI
- JWT secret from Docker secrets (/run/secrets/jwt_secret)
- Updated README with secret generation instructions

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 15:37:35 +00:00
gilesb
a2190801e8 feat: add user registration and JWT authentication
- POST /auth/register - create account
- POST /auth/login - get JWT token
- GET /auth/me - get current user
- POST /auth/verify - verify token (for L1)
- Password hashing with bcrypt
- 30-day JWT tokens

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 14:43:14 +00:00
gilesb
dec5266554 feat: RSA key management for ActivityPub signing
- keys.py: Generate/load RSA-2048 keypairs, sign activities
- setup_keys.py: CLI to generate keys
- Real RsaSignature2017 signing (falls back to placeholder if no keys)
- Public key included in actor profile
- Private keys gitignored

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 13:51:58 +00:00
gilesb
da55bda1a5 feat: L2 ActivityPub server
- Registry for owned assets
- ActivityPub endpoints (webfinger, actor, inbox, outbox)
- Create activities with signatures
- Record L1 runs as owned assets with provenance
- Federation support (followers, inbox)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:32:43 +00:00