Migrations
SPV2 Migration
Roll out Sharing Permissions V2 safely with backfill, dual-read, dual-write, and rollback controls.
Use this guide to migrate from legacy per-resource permissions to SPV2 global principal permissions.
Migration Goals
- Keep authorization decisions stable during rollout.
- Canonicalize legacy user identifiers into principal identifiers.
- Allow rollback if production checks fail.
Runtime Modes
readMode: "v2"(default): Read SPV2 only.readMode: "dual": Read both legacy and SPV2, combine decisions safely.readMode: "v1": Rollback read path to legacy.writeMode: "v2"(default): Write SPV2 only.writeMode: "dual": Write SPV2 and mirror legacy rows.
Rollout Steps
- Deploy code with migration runtime flags available.
- Run backfill from legacy permission tables into global permissions.
- Switch to dual-read and dual-write during verification window.
- Validate equivalent access outcomes and sync visibility.
- Move to
readMode: "v2"andwriteMode: "v2".
Backfill Behavior
Backfill is idempotent:
- Active legacy grants are migrated once.
- Canonical principal format is enforced (
user:bob, not plainbob). - Conflicting duplicate grants fail deterministically with
CONFLICT.
Rollback Plan
If cutover is halted:
- Set
readMode: "v1". - Keep
writeMode: "dual"during investigation. - Resolve data issue.
- Re-run backfill if needed.
- Return to dual-read, then v2-only.
Compatibility Window
Legacy API calls that send shareWith.userId are accepted during compatibility period and canonicalized to shareWith.principalId.
This supports no-downtime upgrades for older clients while migration dashboards track deprecated payload usage.
For operational runbook details, see datafn/.conduct/migrations/spv2-rollout-runbook.md.