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>
Replace ipfshttpclient library with direct HTTP requests to IPFS API.
This fixes compatibility with newer Kubo versions (0.39.0+) which are
not supported by the outdated ipfshttpclient library.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 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>