Skip to content

C API v2, v1 data migration 015, v1 removal (DEV-1150 part 3c) - #53

Merged
adamski merged 83 commits into
mainfrom
adam/dev-1150-ffi
Oct 1, 2026
Merged

adamski merged 83 commits into
mainfrom
adam/dev-1150-ffi

Conversation

@adamski

@adamski adamski commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Puts the v2 engine behind the C API, migrates v1 local data, and removes v1. This is the C API that freezes as ABI 1.0 (library 0.7.0 in 3d).

This branch includes #52 (hardening). Merge #52 first, and this diff shrinks to the 61 commits after it.

What changes

  • Registry and C API v2. The header is generated (include/replicant.h) and checked against the build. The C++ wrapper is include/replicant.hpp.
    • Configs and states are versioned by struct_size. Callback typedefs and constants carry the Replicant prefix.
    • Every exported function is wrapped in catch_unwind.
  • Secrets never cross the API. load_credentials and store_credentials are gone.
    • enroll_claim stores the credentials and returns only the user id. clear signs out.
    • "Signed in" is the state and halt reason plus get_user_id.
  • Credential halts. Every credential halt rechecks the stored credentials every 3 s. Signing out from any of them gives NotEnrolled.
    • create reads the stored credentials before it returns, so the first get_state is correct.
    • Credentials that are missing or can't be decrypted read as signed out. They no longer retry forever.
  • Migration 015. Moves v1 local data to v2 without silent loss.
    • Unsent work that can't be migrated is kept aside as a recovered copy.
    • The v1 file is backed up once and never overwritten. A restored backup gets a fresh one.
  • The library knows nothing about content.
    • Titles come only from ReplicantConfig.title_pointer, a JSON Pointer the app sets. With none set, there are no titles.
    • author_name is gone; documents carry owner_id and author_id.
    • Full-text search stays and gives no special weight to any key.
  • JSON contract. The header documents every key of every JSON result.
    • A document's own id is id; other records refer to it as doc_id. The owner is owner_id.
    • Timestamps are RFC 3339 UTC.
  • v1 removed. The CHANGELOG lists the removed and renamed 0.6 calls, error codes and JSON keys.
  • SQLite. The static library exports its bundled SQLite 3.46.0. Code in the same binary must use it, and it is not part of the ABI (README).

Breaking for consumers

This is a new ABI (1.0). Entonal moves to it in Plan 4. Hosts must:

  • pass the email in the config, needed for 0.6 credentials;
  • set title_pointer;
  • read owner_id instead of user_id.

Verification

  • Tests: lib 614, also 4× with 16 threads. ffi_tests 45, ffi_lifecycle_tests 5, wire_contract_tests 15. v2_smoke compiles.
  • Clippy shows only the 2 baseline warnings. fmt is clean.
  • The header compiles as strict C11 under clang and gcc-14.
  • The wrapper compiles with -Werror as C++17 and C++20 under clang++ and g++-14.
  • A C probe linked against the static library passes 44/44.
  • Reviews: final 3c review, two fix rounds with re-reviews, an ABI freeze review (freeze-ready), and a re-review of the pre-freeze round. All clean or fixed. Each fix has a test that fails when the fix is reverted.

Follow-ups: replicant-sync/replicant-server#12 (boundary cleanup); 3d (interop, release link check per platform, 0.7.0 release).

…n only while positions line up); otherwise the server's list is kept and the local one set aside
… both sides touch overlapping indices, so a shifted diff (a remove plus an insert, or a reorder) can no longer read as agreement; also treat an identical result at a colliding root list as clean, not a conflict
…when both sides changed it and ended up different, since a disjoint-looking edit may really be retargeted at the other side's twin
…ic and checks no element is lost or duplicated at a rebase
…res list conflicts to be whole, and checks element order
… another device supersedes it, and edits made before the delete are kept
…ently revive an element another device removed
…or fetches the server copy first, and a newer version supersedes it
…at raced another process, reports a newer schema, and refuses a server url with a query
… credentials first, and an unreadable credential file is retried, never read as a sign-out
… it visible and read-only, keeping edited content; a Full list policy merges as Atomic
…n first open, after a backup, keeping unreadable rows aside
…nd rename it, keep an existing backup, and leave a busy backup retryable
…laim signs in, clear signs out, sign-in status comes from state and the user id
…sed config with named fields, take_string frees on bad_alloc, ABI-mismatch and callback docs
…, so a sign-in in another process is picked up within about 3 s
…every credential halt re-reads the stored credentials every 3 s without redialling refused ones
…r loss after it, so connection events keep their order
… 3 s and dials only a different account; the disabled one is never redialled
…rites .v1-backup-<unix seconds>), and an unreadable kept copy is skipped instead of failing the whole list
…ps the copy; header documents thread binding of register calls, migration-failed recovery, the 0.6 email requirement, recovered_at units and destroy_and_wait during a pump
… requests before and after, so it no longer races the follow-up request
…d again are dialled; a new secret for the same key counts as new credentials
…fter a non-credential halt, and every dial cancels it
…restored over it; concurrent and failed launches add no copies
… first get_state is already Halted/NotEnrolled without them and the start halt is sent once
…g-thread call never disturbs the owner's pump; header states that a destroy must not overlap any other call on its handle
… input before contacting the server, and the header lists every result it returns
…gine check, so the first handle's email signs the engine's joins
…e 3.46.0, which same-binary code must use and which is not part of the replicant ABI
… every key, type and null case of each JSON result, with RFC 3339 UTC timestamps
…Config.title_pointer (none: no titles), it is part of the shared-engine config, and a launch with a different pointer recomputes every title and the search index
…rom it, and without it the library assigns none
…ryptable, unparseable) count as signed out, so the engine halts NotEnrolled and its 3 s recheck picks up the next sign-in; other read errors keep retrying
…a torn read, since credential writes are atomic
@adamski
adamski merged commit 200402d into main Oct 1, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant