Skip to content

Delete the zingolib submodule from zingo-mobile and consume the Prebuilt Bundles #2930

Open

Description

@zancas

Problem

zingo-mobile holds zingolib as a git submodule under ADR zingo-mobile/0016, and its CI and its release build the Binding Layer from that source. #2833 moved the build into zingolib, which publishes each pinned commit as two Prebuilt Bundles, so the submodule and the five build pieces #2833 names are to be deleted.

The submodule feeds more than the build. Fourteen workflows, two required crate-test checks, the Kotlin wire-contract test, a Jest marker test, two yarn scripts, the R8 keep-verifier, the lint script and the docs all reach into it.

Rulings

Ruled in a grilling session on 2026-10-12 and amended on the reviews that followed. The dates on each ruling name the amendments.

  1. One pin. zingo-mobile pins zingolib in one lock file shaped like kitchen-sync's binding-layer.json. The lock replaces the lock-versus-submodule check of Plan: zingolib publishes the Binding Layer, and zingo-mobile downloads it #2833.
  2. Identical management. kitchen-sync and zingo-mobile manage their bundles identically unless a significant trade-off separates them.
  3. Android consumption. The Android Prebuilt Bundle is a Maven repository layout, and the Bundle Pin carries its coordinate under coordinates.android, which pin-binding-layer copies from the manifest annotation org.zingolabs.zingolib.coordinate without a download (zingo-adrs zingolib/0057 and 0058; zingolib#3030, merged 2026-10-20 as 0a5cb69). Gradle reads the group and the dependency from the pin, resolves JNA from the POM, and runs the fetch, which verifies the extracted layout against the pin, only when the repository resolves through an exclusiveContent filter on the group.
  4. iOS consumption. The fetch extracts the Swift package to a fixed path that Xcode references as a local package, guarded by a digest marker. The extraction landed in kitchen-sync first.
  5. Separate fetch jobs. One required fetch job per platform runs the tag check, and every consuming job pulls anonymously by digest. No Actions cache stands in front of the pulls until a measurement calls for one.
  6. Tests move to zingolib. The crate tests, the live mixnet start and the wire-contract tests run in zingolib's CI, with the mixnet marker pinned where it is minted. zingo-mobile drops its copies only after zingolib runs them.
  7. Release. The pinned AAR is the release's only native input. The R8 keep-verifier reads its class list from the AAR's classes.jar.
  8. Developer path. A Binding Layer change is published as a work-in-progress commit through Plan: zingolib publishes the Binding Layer, and zingo-mobile downloads it #2833, decision 5, and pinned. There is no local build and no override.
  9. Records. zingo-adrs zingo-mobile/0020 supersedes 0016 and binds zingo-mobile alone, while ruling 2 stays the rule for every consumer. zingo-mobile's CONTEXT.md takes the Binding Layer vocabulary from zingolib's glossary by reference.
  10. The lock and the submodule agree. While both exist, the lock and the submodule name one commit, so the three readers test the zingolib the bundles were built from. Ruled 2026-10-20: the submodule is not bumped on its own again, step 4 sets both to its entry in one change, and this ruling's wait on a stable release (stable still ends at bfb0a21) is owed a re-examination under zingolib#2988.

Deferred

  • How the fetch scripts are shared. Decided 2026-10-12: one package, zingolib-bundle-pin, that every consumer installs as a development dependency. From 0.4.0 the consumer runs its two commands through two package.json scripts copied from the README, which run Node 22.13 with --permission opened to the paths each command touches, and Gradle, Xcode and CI call those scripts rather than the node_modules/.bin links.
  • Where the package lives and how it is published. Decided 2026-10-12: zingolib bindings/pin, published to npm by bundle-pin.yaml on each push to dev whose version npm lacks. Versions 0.1.0 to 0.3.0 are on npm, and 0.4.0 is in progress on 2026-10-20 on branch feat/pin-capability-bound, uncommitted and without a pull request: it extracts in process, refuses every archive not in the pax tar format, and makes the publisher pack in pax, so no entry published before that change fetches at 0.4.0.
  • JNA in the lock. Decided 2026-10-15 against an annotation: the Maven layout's POM carries JNA and Gradle resolves it (zingo-adrs zingolib/0057, zingolib#2979). Amended 2026-10-20: the lock restates one coordinate, the Binding Layer's own, under ruling 3.

Sequence

Each step is green before the next starts. A piece of the new architecture works before the matching piece of the old one is deleted, and the one step that bends this says so.

  • 1. zingolib. zingolib#2937 (b5b2755) runs both wire-contract tests on the shipped Binding Sets and adds the glossary terms, and zingolib#2970 (3ac96c4) carried its review findings. Nothing was deleted.
  • 2. zingo-adrs. zingo-adrs#36 (67f5de3) adds zingo-mobile/0020 as proposed. Step 6 flips 0020 to accepted and 0016 to superseded by.
  • 3. kitchen-sync. kitchen-sync#2 proved the iOS extraction, zingolib#2941 (70362bd) added the package, kitchen-sync#3 (b126732) installed it, and kitchen-sync#4 (224ac03d4) consumes the Maven layout. A follow-up to the current package version is owed before step 4.
  • 4. zingo-mobile, consumption. Set the lock and the submodule to an entry at or past 7177de9 (zingolib#3026, which zingo-mobile#1697 needs), install zingolib-bundle-pin at its current version (0.4.0 once it merges), and switch every consumer, both nightlies included, to the fetched bundles, with one dispatched nightly passing before step 5. Warning: this step drops the submodule's includeBuild in the PR that proves the AAR, since Gradle cannot hold both, while the old build still runs so one revert restores it.
  • 5. zingo-mobile, release. Take the AAR as the release's only native input and read the keep-verifier's class list from classes.jar while the submodule still exists. Prove it with a tag on a test channel before step 6.
  • 6. zingo-mobile, deletion. Delete the five named pieces, the zingolib-submodule action, binding-layer-check.yaml, the kotlin-bindings consumers, the submodule with its three readers, yarn rust:android, yarn rust:ios, and every submodule reference in scripts and docs. Update the glossary, advance the docs/adr pointer, and rewrite branch protection as the PR merges.

Related

Next step

Steps 1 to 3 are done. Three things precede step 4, in this order.

  1. Publish an entry at a dev commit past the merges of zingolib#3033 and the 0.4.0 change, so it carries the coordinate annotation, the sixteen-character descriptor, the pax archive and 7177de9. No entry published before them pins for Android at 0.3.0 or fetches at 0.4.0.
  2. kitchen-sync moves to zingolib-bundle-pin 0.4.0, with its two package.json scripts and Node 22.13, and pins that entry. Step 4 then copies the pin-file wiring.
  3. The user re-examines ruling 10's wait on a stable release. Step 4 starts on that ruling.

Updated 2026-10-20.

Activity

  1. juanky201271 commented on Oct 12, 2026

    @juanky201271
    Contributor

    Checked the plan against zingo-mobile dev (b84a7d5b4), zingolib dev (0a71c70) and kitchen-sync. The inventory is right. Five things change the steps:

    1. Step 1 is mostly done. zingo-ffi/lib is a root-workspace member, so test.yaml already runs the wallet crate tests on every PR; netutils-standalone already runs the proxy crate tests and the live mixnet start; and the marker is pinned in zingo-ffi since a67de8d (a_mixnet_refusal_maps_to_the_owned_mixnet_marker asserts starts_with("Error: mixnet:")). What remains is the Kotlin wire-contract test, which needs a JVM harness with the host library in zingolib, and the Swift one, which has never run anywhere (it is gated on send-over-nym, step 7 of 7 (Mac-gated, last): iOS transport, hosting, and device smoke #2504).

    2. Step 4 needs a word on the pin. dev now pins 1470762, the published entry is ba73367, and the crates are identical between them. Either the submodule moves to ba73367 in the same PR as the lock, or the step says the two may differ while they coexist. With no lock-vs-submodule check, the three readers would otherwise test a different zingolib than the bundle.

    3. ios-build.yaml has no "build half": its three jobs are all Binding Layer build and cache, and the app build lives in ios-integration-test.yaml. Either the whole file goes, or the cache jobs become the iOS fetch job. Say which.

    4. Ruling 9 references zingolib's glossary for Prebuilt Bundle, Bundle Pin and Binding Set, and neither of zingolib's two glossaries defines them. Today only kitchen-sync's docs/architecture.md does. One step has to add them to zingolib first.

    5. Ruling 5 caches the bundles by digest; kitchen-sync fetches from ghcr on every job with no cache. That breaks ruling 2 unless kitchen-sync adopts the cache too, and the bundles are 253 MiB (AAR) and 362 MiB (iOS) on a cache that is already over budget. An anonymous ghcr pull verified by digest is already a cache; I would measure before adding one.

    Minor: JNA is not in the lock, so a pin bump can desync it silently; the publish could annotate the OCI manifest with it.

  2. zancas commented on Oct 12, 2026

    @zancas
    MemberAuthor

    Thanks, checked each claim against the repositories. Three held, and the plan above is amended: ruling 5 drops the cache until a measurement calls for one (253 MiB and 362 MiB measured on the registry), ruling 9 names the glossary gap, and the iOS wording in #2833 is corrected. One held in part: zingolib's glossary does define Prebuilt Bundle, but not Bundle Pin or Binding Set, and step 1 adds them. One did not hold: the zingolib crate differs between dev's pin and the published entry by the sapling-params change of #2914, which strengthens your point, so ruling 10 now requires the lock and the submodule to agree while both exist, and step 4 publishes a new entry at the pin or newer. JNA in the lock is recorded as a deferred item with your proposal as the candidate answer.

    Step 1 is #2937.

  3. zancas commented on Oct 12, 2026

    @zancas
    MemberAuthor

    Ruling 9 is amended on the review of zingo-adrs#36. The earlier text said the new zingo-mobile/ record binds every consumer of the Prebuilt Bundles, and a repo-scoped record binds one repository. The record now states zingo-mobile's own commitment to manage its bundles as kitchen-sync does, and ruling 2 stays the rule for every consumer until an org-scoped record holds it. zingo-adrs 2d3c0ab carries the matching change to decision 2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions