Skip to content

Migrate legacy signed-out device identifiers - #360

Merged
rajzik merged 6 commits into
mainfrom
codex/issue-271-migrate-signed-out-device-identifiers
Aug 8, 2026
Merged

rajzik merged 6 commits into
mainfrom
codex/issue-271-migrate-signed-out-device-identifiers

Conversation

@gipity-bot

@gipity-bot gipity-bot Bot commented Aug 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add a one-time internal importer for legacy Trusted Device identifier history
  • gate new revocations on a per-account migration-complete marker while keeping existing tombstoned accounts fail-closed
  • preserve exact imported reconnects and continue rejecting revoked or genuinely unseen identifiers
  • document the retained-export source and rollout order, with a Convex patch changeset

Why

Legacy sign-out deleted the only backend row containing an installation identifier. Once an account had its first revocation tombstone, the backend could not distinguish that previously known installation from a newly minted identifier. Guessing or relaxing the lock would weaken revocation; enforcing it without migration would permanently reject legitimate reconnects.

Impact

Operators must import retained pre-enforcement Trusted Device exports before completing migration for an existing Product Account. Existing accounts cannot begin another revocation until completion, and completed migrations cannot admit additional identifiers. New Product Accounts are complete at creation.

Validation

  • mise exec -- pnpm lint
  • mise exec -- pnpm format
  • mise exec -- pnpm turbo run check-types
  • mise exec -- pnpm test (185 tests passed)
  • mise exec -- pnpm fallow (zero issues)
  • git diff --check

Closes #271


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: cc92f5c6-e693-4c1f-a4e3-5b832903cc3b

📥 Commits

Reviewing files that changed from the base of the PR and between dded2cb and 05b4f42.

📒 Files selected for processing (1)
  • packages/convex/test/productAccount.test.ts
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use Oxlint with @rajzik/oxlint-config for TypeScript linting, Oxfmt with @rajzik/oxfmt-config for formatting, and extend @rajzik/tsconfig for TypeScript configuration.
Use existing abstractions and conventions before introducing new ones; prefer the minimum code that solves the requested problem and avoid speculative features, single-use abstractions, and unrequested configurability.
When editing TypeScript, make surgical changes: do not refactor unrelated code or formatting, match existing style, and remove only imports, variables, or functions made unused by the change.
Use the mise-managed repository toolchain for TypeScript validation; trust .mise.toml and run mise install, or invoke commands through mise exec -- when mise is not activated.

Files:

  • packages/convex/test/productAccount.test.ts
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

For ad hoc runnable TypeScript, use a temporary file under scratchpad/, run it with node scratchpad/<file>.ts, and delete it afterward; use Node 24 directly instead of tsx unless Node fails.

Files:

  • packages/convex/test/productAccount.test.ts
packages/convex/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Treat packages/convex as the backend package and follow the repository TypeScript tooling and validation requirements for its Convex code.

Files:

  • packages/convex/test/productAccount.test.ts
packages/*/test/**/*.ts

⚙️ CodeRabbit configuration file

packages/*/test/**/*.ts: Verify externally observable behavior, authorization and privacy
invariants, malformed inputs, and deterministic isolation. Flag tests that
duplicate the implementation or omit meaningful negative cases.

Files:

  • packages/convex/test/productAccount.test.ts
🔇 Additional comments (1)
packages/convex/test/productAccount.test.ts (1)

186-196: LGTM!

Also applies to: 198-326, 1769-1787, 1789-1804, 1806-1833


📝 Walkthrough

Summary

  • Adds a one-time internal Convex importer for legacy signed-out Trusted Device identifiers.
  • Requires migration completion before existing accounts can revoke devices.
  • Preserves fail-closed behavior for accounts with revocation tombstones.
  • Allows reconnects only for exact imported identifiers after migration.
  • Rejects revoked and unseen identifiers after account-wide revocation enforcement.
  • Initializes new accounts as migration-complete.

Affected areas

  • TypeScript: Convex product account mutations, Trusted Device registration and revocation, schema, tests, ADR, and changeset.
  • Apple: Legacy Trusted Device reconnect behavior for installations signed out before deployment. No Apple source changes are included.

Migration and operations

  • Operators import retained identifiers in batches of up to 100 per Product Account.
  • Operators mark each account complete with a final batch.
  • The ADR documents the retained-export source and rollout order.
  • Accounts with unavailable or incomplete inventories remain gated.

Validation

  • Lint, formatting, type checks, 185 tests, Fallow, and git diff --check passed.

Walkthrough

This change migrates legacy Trusted Device identifiers, records per-account completion, and gates reconnection and revocation until migration completes. New accounts start complete, while tombstoned accounts remain fail-closed.

Changes

Trusted-device migration

Layer / File(s) Summary
Migration state and identifier import
packages/convex/convex/schema.ts, packages/convex/convex/productAccount.ts, docs/adr/..., CONTEXT.md, .changeset/...
Product accounts record migration completion. An internal mutation imports identifiers in batches of up to 100, updates identifier history, and records completion. Documentation describes the rollout sequence.
Reconnect and revocation enforcement
packages/convex/convex/productAccount.ts, packages/convex/test/productAccount.test.ts
Migrated identifiers can reconnect after completion. Unseen and revoked identifiers remain blocked. New revocations require completed migration. Tests cover tombstones, incomplete migrations, batch limits, and unknown accounts.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant migrateLegacyTrustedDeviceIdentifiers
  participant ProductAccount
  participant TrustedDeviceRegistration
  Operator->>migrateLegacyTrustedDeviceIdentifiers: Import retained legacy identifiers
  migrateLegacyTrustedDeviceIdentifiers->>ProductAccount: Store identifier history and completion marker
  TrustedDeviceRegistration->>ProductAccount: Check identifier and migration state
  ProductAccount-->>TrustedDeviceRegistration: Allow migrated identifier or reject unseen identifier
Loading

Suggested reviewers: rajzik

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: migrating legacy signed-out device identifiers.
Description check ✅ Passed The description directly explains the migration, revocation gating, reconnect behavior, documentation, and validation.
Linked Issues check ✅ Passed The changes address issue #271 through migration logic, rollout documentation, reconnect protection, revocation enforcement, and comprehensive tests.
Out of Scope Changes check ✅ Passed The changeset, context, ADR, schema update, implementation, and tests all support the linked issue objectives.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/issue-271-migrate-signed-out-device-identifiers

Comment @coderabbitai help to get the list of available commands.

@rajzik
rajzik marked this pull request as ready for review August 8, 2026 06:33
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@rajzik

rajzik commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ad737908c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/convex/convex/productAccount.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/convex/test/productAccount.test.ts`:
- Around line 1760-1782: Add two validation tests near the existing legacy
identifier migration tests: one invoking migrateLegacyTrustedDeviceIdentifiers
with 101 identifiers and asserting the batch-size error, and one invoking it
with an unknown tokenIdentifier and asserting the Product Account required
error. Follow the existing convexTest/schema setup and assertion conventions.
- Around line 186-193: Refactor expectLegacyIdentifierMigration to accept one
scenario object containing existingRevocationTombstone, migrationComplete, and
completeAfterFirstBatch, making the migration contract explicit. In the helper
body, read scenario.existingRevocationTombstone and scenario.migrationComplete,
and invoke scenario.completeAfterFirstBatch only when migrationComplete is
false; update both callers accordingly.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8a04a7f1-7afc-4b88-86f1-6e99c94e31bc

📥 Commits

Reviewing files that changed from the base of the PR and between bf42eaa and dded2cb.

📒 Files selected for processing (6)
  • .changeset/migrate-legacy-trusted-device-identifiers.md
  • CONTEXT.md
  • docs/adr/0020-revoke-devices-with-sync-key-rotation.md
  • packages/convex/convex/productAccount.ts
  • packages/convex/convex/schema.ts
  • packages/convex/test/productAccount.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (9)
.changeset/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Create a Changeset for runtime behavior changes or exported type/API changes; tests-only, internal refactors, documentation-only changes, and JSDoc-only maintenance may omit one at maintainer discretion.

Files:

  • .changeset/migrate-legacy-trusted-device-identifiers.md
packages/convex/convex/**/*.ts

📄 CodeRabbit inference engine (packages/convex/CLAUDE.md)

For work on Convex code, always read convex/_generated/ai/guidelines.md first; its rules override general Convex knowledge when using Convex APIs and patterns.

For Convex code, always read convex/_generated/ai/guidelines.md first; its rules override general Convex knowledge when using Convex APIs and patterns.

Files:

  • packages/convex/convex/schema.ts
  • packages/convex/convex/productAccount.ts

⚙️ CodeRabbit configuration file

packages/convex/convex/**/*.ts: Treat authentication, authorization, input validation, and privacy
boundaries as mandatory. Flag public functions that do not validate
arguments and returns, data access that is not scoped to the authenticated
Product Account or trusted device, plaintext sensitive data, secrets in
logs, unbounded reads, and queries that bypass appropriate indexes.
Changes to runtime behavior should include focused Convex tests covering
success, unauthorized access, malformed input, and relevant retries.

Files:

  • packages/convex/convex/schema.ts
  • packages/convex/convex/productAccount.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use Oxlint with @rajzik/oxlint-config for TypeScript linting, Oxfmt with @rajzik/oxfmt-config for formatting, and extend @rajzik/tsconfig for TypeScript configuration.
Use existing abstractions and conventions before introducing new ones; prefer the minimum code that solves the requested problem and avoid speculative features, single-use abstractions, and unrequested configurability.
When editing TypeScript, make surgical changes: do not refactor unrelated code or formatting, match existing style, and remove only imports, variables, or functions made unused by the change.
Use the mise-managed repository toolchain for TypeScript validation; trust .mise.toml and run mise install, or invoke commands through mise exec -- when mise is not activated.

Files:

  • packages/convex/convex/schema.ts
  • packages/convex/test/productAccount.test.ts
  • packages/convex/convex/productAccount.ts
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

For ad hoc runnable TypeScript, use a temporary file under scratchpad/, run it with node scratchpad/<file>.ts, and delete it afterward; use Node 24 directly instead of tsx unless Node fails.

Files:

  • packages/convex/convex/schema.ts
  • packages/convex/test/productAccount.test.ts
  • packages/convex/convex/productAccount.ts
packages/convex/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Treat packages/convex as the backend package and follow the repository TypeScript tooling and validation requirements for its Convex code.

Files:

  • packages/convex/convex/schema.ts
  • packages/convex/test/productAccount.test.ts
  • packages/convex/convex/productAccount.ts
packages/*/test/**/*.ts

⚙️ CodeRabbit configuration file

packages/*/test/**/*.ts: Verify externally observable behavior, authorization and privacy
invariants, malformed inputs, and deterministic isolation. Flag tests that
duplicate the implementation or omit meaningful negative cases.

Files:

  • packages/convex/test/productAccount.test.ts
CONTEXT.md

📄 CodeRabbit inference engine (AGENTS.md)

Update CONTEXT.md when product language, domain terms, or resolved ambiguity changes.

Files:

  • CONTEXT.md
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Treat docs as product truth, not marketing. Prefer precise domain language
from CONTEXT.md; flag undefined terms, synonym drift, and conflicts with
existing ADRs under docs/adr/. For ADR changes, require a clear decision,
consequences, and privacy or security boundary impact; flag silent
reversals of prior ADRs without an explicit supersession note. For
docs/agents/ and bootstrap docs, verify agent workflow, validation, and
triage guidance stay aligned with AGENTS.md. Flag broken cross-links,
speculative future claims presented as shipped behavior, and docs that
omit the Apple vs TypeScript ownership of a boundary they describe.

Files:

  • CONTEXT.md
  • docs/adr/0020-revoke-devices-with-sync-key-rotation.md
docs/adr/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Record durable architecture decisions and privacy-boundary decisions in docs/adr/.

Files:

  • docs/adr/0020-revoke-devices-with-sync-key-rotation.md
🪛 markdownlint-cli2 (0.23.2)
.changeset/migrate-legacy-trusted-device-identifiers.md

[warning] 5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (11)
packages/convex/convex/schema.ts (1)

9-9: LGTM!

packages/convex/convex/productAccount.ts (6)

16-16: LGTM!

Also applies to: 35-35, 54-58


297-297: LGTM!


318-356: LGTM!


358-425: LGTM!


514-532: LGTM!


1189-1193: LGTM!

docs/adr/0020-revoke-devices-with-sync-key-rotation.md (1)

5-6: LGTM!

CONTEXT.md (1)

678-679: LGTM!

.changeset/migrate-legacy-trusted-device-identifiers.md (1)

1-5: LGTM!

packages/convex/test/productAccount.test.ts (1)

1745-1757: 📐 Maintainability & Code Quality

No change required

trustedDeviceCredential is optional. Both devices are connected without supportsDeviceCredentials, so the omitted credential does not block authentication. The call reaches the migration check.

			> Likely an incorrect or invalid review comment.

Comment thread packages/convex/test/productAccount.test.ts
Comment thread packages/convex/test/productAccount.test.ts
@rajzik
rajzik merged commit 2e2a5ca into main Aug 8, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate signed-out Trusted Device identifiers before enforcing revocation lock

1 participant