step 1: Etebase-compatible file-backed server, green against the Rust suite #1

Open
bizon-agent wants to merge 12 commits from step1-server into main
Member

Step 1 of SPEC §9: the Etebase-compatible server, file-backed, green against the Etebase Rust core's integration suite. Five commits, spec first:

  1. SPEC: step-1 amendment — §5 on-disk format (mysync-data 1), §6 server API, §7 escape hatch. Its own commit, before any code. Decisions it pins (confirm at merge): email is not stored and returns empty; sharing endpoints answer empty lists or not_supported; the sync token is one decimal counter per account; the test-reset route exists only behind serve --test-reset; tokens are <account>:<secret>.
  2. Skeleton — scripts/go-image (golang 1.26-bookworm by digest), scripts/ci-gates.sh (the one copy of the gates), ci.yml, release-on-merge.yml (publishes the binary and checksum, no deploy job), internal/msgpack pinned to the spec vectors.
  3. internal/store — §5: accounts, hashed token files, per-collection append-only log whose seq is the sync token, chunk files, crash-remnant handling, idle eviction, caps.
  4. internal/api + serve + tests/conformance/run.sh — §6, with in-process Go tests over every endpoint.
  5. mysync dump + tests/fixture — the §4 allow-list gate over a data directory written by the real client (300 contacts, 500 events, 150 notes).

Receipt (workstation, 2026-10-21, ./scripts/dev-check.sh):

  • gates 0–4 green in the pinned image (go vet, go test ./..., build, gofmt -l empty)
  • gate 5: etebase-rs v0.6.1 (c0c74b7) suite against a boxed server: 22 passed, 0 failed, 2 ignored upstream, 3 sharing tests skipped by name (SPEC §6.9)
  • mysync dump over the data directory after the suite and the fixture: allow-list OK, 8646 fields and files, nothing outside SPEC §4
  • boxed --check: no leftovers

Not in this PR: mysync decrypt (SPEC §7). Etebase's KDF is libsodium crypto_kdf = BLAKE2b keyed with salt and personalization; golang.org/x/crypto/blake2b exposes neither. The module decision is fork A in ~/forks/mysync-2026-10-21.md (recommended: copy x/crypto/blake2b into internal/ and add the two parameter-block fields, vectored against the Rust oracle). SPEC and CLAUDE.md say so.

Not deployed, not merged. CI needs Actions enabled on this repo (grant still pending).

🤖 Generated with Claude Code

Step 1 of SPEC §9: the Etebase-compatible server, file-backed, green against the Etebase Rust core's integration suite. Five commits, spec first: 1. `SPEC: step-1 amendment` — §5 on-disk format (`mysync-data 1`), §6 server API, §7 escape hatch. Its own commit, before any code. Decisions it pins (confirm at merge): email is not stored and returns empty; sharing endpoints answer empty lists or `not_supported`; the sync token is one decimal counter per account; the test-reset route exists only behind `serve --test-reset`; tokens are `<account>:<secret>`. 2. Skeleton — `scripts/go-image` (golang 1.26-bookworm by digest), `scripts/ci-gates.sh` (the one copy of the gates), `ci.yml`, `release-on-merge.yml` (publishes the binary and checksum, no deploy job), `internal/msgpack` pinned to the spec vectors. 3. `internal/store` — §5: accounts, hashed token files, per-collection append-only log whose `seq` is the sync token, chunk files, crash-remnant handling, idle eviction, caps. 4. `internal/api` + `serve` + `tests/conformance/run.sh` — §6, with in-process Go tests over every endpoint. 5. `mysync dump` + `tests/fixture` — the §4 allow-list gate over a data directory written by the real client (300 contacts, 500 events, 150 notes). **Receipt (workstation, 2026-10-21, `./scripts/dev-check.sh`):** - gates 0–4 green in the pinned image (`go vet`, `go test ./...`, build, `gofmt -l` empty) - gate 5: etebase-rs v0.6.1 (`c0c74b7`) suite against a boxed server: **22 passed, 0 failed, 2 ignored upstream, 3 sharing tests skipped by name** (SPEC §6.9) - `mysync dump` over the data directory after the suite and the fixture: **allow-list OK, 8646 fields and files, nothing outside SPEC §4** - `boxed --check`: no leftovers **Not in this PR:** `mysync decrypt` (SPEC §7). Etebase's KDF is libsodium `crypto_kdf` = BLAKE2b keyed with salt and personalization; `golang.org/x/crypto/blake2b` exposes neither. The module decision is fork A in `~/forks/mysync-2026-10-21.md` (recommended: copy `x/crypto/blake2b` into `internal/` and add the two parameter-block fields, vectored against the Rust oracle). SPEC and CLAUDE.md say so. **Not deployed, not merged.** CI needs Actions enabled on this repo (grant still pending). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Amendment of 2026-10-21, written before any server code (CLAUDE.md: spec first).
Pins what the upstream protocol pages leave open (items, invitations are TBD
upstream) from the Etebase Rust core c0c74b7 and its integration suite, which
is the known answer for step 1. Decisions taken here, for the operator to
confirm at merge: email is not stored and returns empty; sharing endpoints
answer empty lists or not_supported; the sync token is one decimal counter per
account; the test-reset route exists only behind --test-reset; the escape hatch
uses x/crypto inside the server binary whatever the client core language.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
scripts/go-image pins golang:1.26-bookworm by digest; scripts/ci-gates.sh is
the one copy of the gates (toolchain allow-list, vet, test, build the one
binary, gofmt) run by ci.yml, release-on-merge.yml and scripts/dev-check.sh,
mirroring shelf. No deploy job: deployment is a later hub request.
internal/msgpack is the wire codec, pinned to the msgpack spec vectors.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Accounts, tokens (hashed files), collections, the append-only revision log
whose seq is the sync token, chunk files, crash-remnant handling, idle
eviction, caps. Tests reload from disk and compare with memory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Challenge-response login (stdlib Ed25519, HMAC challenge), collections,
items with the batch/transaction rules of §6.5, revisions, fetch_updates,
chunks, the §6.6 no-sharing answers, the --test-reset route. In-process Go
tests cover every endpoint; tests/conformance/run.sh runs etebase-rs v0.6.1's
suite against a boxed server: 22 passed, 0 failed, 2 ignored upstream, the 3
sharing tests skipped by name.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
internal/dump inventories every file and field of a data directory against
the §4 allow-list and bounds every binary value by a randomness test; the
dump subcommand prints the table and exits 1 on anything outside the list.
tests/fixture (Rust, on the etebase crate at the pinned commit) writes 300
contacts, 500 events and 150 notes through the real client; conformance
runs it after the suite and then dump. CLAUDE.md, README and the SPEC status
say what step 1 built and that decrypt is parked on the BLAKE2b fork.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
EteSync 2.7.0 (F-Droid) on the Pixel 8 Pro-shaped emulator signed up against
the boxed server, created its collections and synced a contact and an event;
the data directory passed mysync dump. README status, INTENT item 10 (built),
CLAUDE.md Android notes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Per the setup review of 2026-10-22 (forks/2026-10-22-setup-review.md): the design
record and the step-1 fork record move from ~/forks into forks/ so they are in git.
~/src/mysync/forks is now a symlink to main/forks so the review's paths still resolve.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
From the operator's answers of 2026-10-22 (forks/2026-10-21-step1-server.md, 'Answered
2026-10-22') and the setup review (forks/2026-10-22-setup-review.md):

- §3: 'One binary, two roles' replaces the home-box bullet: serve for the phone; at home
  the same binary mirrors the account as ciphertext or as two-way plaintext folders;
  shelf is not an Etebase client; the home role is pull-only.
- §3: 'The one patched primitive': internal/blake2b, a copy of x/crypto/blake2b with salt
  and personalization made settable, diffed against upstream by a gate and vectored
  against libsodium through the Rust oracle. Answers the fork that blocked §7.
- §7: no longer blocked; decrypt is the next thing built. Status paragraph updated.
- §9 step 4: two-way sync at home is demanded, not conditional.
- CLAUDE.md: the exception and the two roles; INTENT: entries 11-13, statuses of 5, 6, 9.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the operator's answers recorded in forks/2026-10-22-dav-adapter.md (forks 1-8 "proceed as
recommended"; forks A-C "as recommended", confirmed in this repo's session):

- §3: the home role's only listener is a CalDAV/CardDAV adapter on 127.0.0.1, no TLS or login,
  one account per process; it is the only writer of the plain contacts/calendar folders, and
  Radicale is replaced by a one-time import. shelf's save path moves to it unchanged.
- §3: built on emersion/go-webdav; its gaps (If-Match evaluation, MKCALENDAR, sync-collection)
  filled in mysync's own backend and offered upstream, never forked; the rest refused.
- §3: the four adapter modules (go-webdav, go-ical, go-vcard, rrule-go) join x/crypto on the
  allow-list, imported only by the adapter's package.
- §9 step 4: the known answers are a differential run against Radicale 3.8.3, real-client
  acceptance (Thunderbird, DAVx5, shelf), and one boxed try of CalDAVTester.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin step1-server:step1-server
git switch step1-server
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
varasys/mysync!1
No description provided.