feat(cli,portability): attestix export — produce M6-compatible bundles (closes symmetric portability)#95
Conversation
…s (closes symmetric portability) Adds the OSS-side exporter symmetric to the importer that landed in PR #93. Closes the persona-review critical: OSS users could import but not export, violating the constitutional zero-lock-in promise. - New attestix export <output.tar.gz> CLI subcommand with --workspace, --include-anchors/--include-audit, --force, --no-pretty options. - New attestix/portability/bundle_writer.py implements write_bundle() as the inverse of importer.py - reads through Repository, projects each OSS row to the cloud wire-format shape, JCS-canonicalises via the shared auth.crypto.canonicalize_json (no second canonicaliser), and emits a USTAR + gzip tarball that bundle_reader + Importer accept byte-cleanly. - Deterministic output: alphabetical member order, USTAR mtime=0, gzip header mtime=0; synthetic cloud UUIDs derived via UUID5 over row content so two consecutive exports of the same OSS state are byte-identical. - Audit-chain integrity: workspace.slug is inferred from the audit_events collection's tenant (or --workspace override) so the importer's chain re-verification round-trips cleanly. - EXPORT_PLAN mirrors the cloud worker's EXPORT_TABLE_SPECS order so the manifest tables array and on-disk member order are byte-comparable across cloud and OSS implementations. - 12 new tests (6 round-trip + 6 CLI smoke). Suite goes 507 -> 519 passing.
|
Warning Review limit reached
More reviews will be available in 39 minutes and 4 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
Note
|
Deploying attestix with
|
| Latest commit: |
da2bda4
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://be60bd7c.attestix.pages.dev |
| Branch Preview URL: | https://feature-cli-bundle-export.attestix.pages.dev |
| import gzip | ||
| import hashlib | ||
| import io | ||
| import json |
|
|
||
| import hashlib | ||
|
|
||
| import pytest |
|
|
||
| from __future__ import annotations | ||
|
|
||
| import pytest |
| from attestix.portability import ( | ||
| Bundle, | ||
| Importer, | ||
| read_bundle, | ||
| write_bundle, | ||
| ) |
| the FileRepository state by clearing the documents (the fixture's tmp_path | ||
| persists for the duration of the test), then import and re-verify. | ||
| """ | ||
| seeded = _seed_oss_data() |
| audit_count_pre = _count_audit_rows() | ||
|
|
||
| out = tmp_path / "seeded.tar.gz" | ||
| result = write_bundle(out) |
What
OSS
attestix export <output.tar.gz>— the symmetric counterpart toattestix import(PR #93) and the cloud M6 P1 worker. Closes the constitutional "portability is a right" principle end-to-end: OSS → cloud and cloud → OSS, byte-interchangeable bundles.This is the persona-review's #1 critical fix — the Bedrock cross-family adversary (GLM-5) caught that the previous tier matrix accidentally gave OSS users import-only portability, violating the zero-lock-in promise.
CLI
[✓] audit_events N rows sha256:abc12345….wrote <path> <bytes> manifest_sha256=<…>.--force.Wire-format parity (https://attestix.io/spec/bundle/v1)
Mirrors the cloud worker
apps/workers/ts/src/exports.tsfield-for-field so cloud↔OSS bundles are byte-interchangeable. Member order matches the cloud'sEXPORT_TABLE_SPECS. USTARmtime=0+ gzip headermtime=0+ alphabetical member order → byte-identical re-exports (verified bytest_deterministic_re_export_is_byte_identical).EXPORT_PLAN (in
attestix/portability/bundle_writer.py)identitiesidentities.agents_row_identity_to_cloudcredentialscredentials.credentials_row_credential_to_cloudcompliance_profilescompliance.profiles_row_compliance_profile_to_cloudconformity_assessmentscompliance.assessments_row_conformity_assessment_to_cloudaudit_eventsaudit.events_row_audit_event_to_cloudanchorsanchors.anchors_row_anchor_to_cloudCloud-only tables (key_references, credential_schemas, memberships, team_invites, subscriptions, agent_dependencies, webhook_endpoints) are emitted as empty JSONL so the manifest count stays cloud-symmetric.
Audit chain integrity
The exporter inspects
audit.eventsand uses each event'stenant_idas the bundle'sworkspace.slug. If multiple tenants exist without--workspacefilter, errors. This guarantees the importer's_project_audit_eventre-tags rows with the same tenant the chain was originally hashed under — soverify_chainround-trips cleanly.Round-trip metadata preservation
Rows that carry
_cloud_id/_cloud_workspace_id(stamped by a prior import) reuse those values rather than synthesising new ones — so a cloud → OSS → export cycle preserves the same cloud UUIDs. OSS-only rows that never round-tripped get UUID5 over(table, row_id)via a frozen namespace for stable hashes.No second canonicaliser
Reuses
attestix.auth.crypto.canonicalize_jsoneverywhere (manifest sha-256, JSONL row sha-256, audit-chain integrity check).Test plan
tests/portability/test_export_import_roundtrip.py(6) — seed → export → import → row-count and chain-integrity match; empty store; partial workspace export;--no-include-anchors; deterministic byte-identical re-export.tests/cli/test_export_command.py(6) — CLI writes file + exit 0; refuses on existing target;--forceproceeds; round-trip throughattestix import --verify-only.Deferred to next slice
.tar.zstoutput (cloud worker is also.tar.gzonly today — P2 parity).website/content/docs/portability/export.mdxdoc page.