# VERSION RECONCILIATION

**BB2G Election Command**
Written 2026-08-15. Every claim below was measured, not assumed.

The blueprint's central architectural argument:

> "The central architectural mistake to avoid is creating sixty copies of
> authentication, evidence handling, audit logging, jurisdiction logic and
> synchronization. The sixty modules should be comparatively thin domain
> applications. **The hard infrastructure should live once.**"

This document answers one question for each file that carries logic in this
build: **where did this code come from, and which copy is canonical?**

---

## 1 · Summary of provenance

| File | Origin | Canonical? |
|---|---|---|
| `assets/crypto.mjs` | **Lifted from `~/ballot-trail/assets/mblts.mjs`** — the engine live at `ballottrail.bornbetween2generals.com` | **No.** `ballot-trail` is upstream. This is a subset copy, byte-identical except two labels. |
| `assets/lockchain.mjs` | **Written new** against the blueprint's LockChain Core specification | Canonical here. No upstream located. |
| `assets/authority.mjs` | **Written new** against the blueprint's authority-resolution correction | Canonical here. No upstream located. |
| `assets/registry.mjs` | **Generated from the blueprint text** — module list, groups, services, dimensions | Canonical here. |
| `assets/house.css`, `motion.css`, `motion.js` | BB2G house style / Lure motion system | Upstream is `bb2g-house`. Not reconciled by this document. |

---

## 2 · `assets/crypto.mjs` — lifted verbatim, and proved so

### Why it was lifted rather than reimplemented

Reimplementing Ed25519, CanonicalJSON and a Merkle construction for a second
election product would have been the exact fault the blueprint names. The
`ballot-trail` engine was already written, already shipped at
`ballottrail.bornbetween2generals.com`, and already gated against the RFC 8032
test vectors. Writing a second one would have produced two implementations that
could disagree, in a product whose entire value proposition is that a record can
be independently verified.

So the primitives were **copied, not rewritten** — and the copy is checkable.

### Which sections were taken

`mblts.mjs` is organised into seven numbered sections. Four came across. Three
did not. The section numbering in `crypto.mjs` **still skips 4** — it runs
1, 2, 3, 5 — which is itself a provenance tell left deliberately in place.

| § | Section | Taken? |
|---|---|---|
| 1 | bytes, hex, hashing | **yes** |
| 2 | CanonicalJSON | **yes** |
| 3 | Ed25519 (RFC 8032), pure BigInt | **yes** |
| 4 | The lifecycle state machine (mail-ballot states/transitions) | no — ballot-domain, not shared |
| 5 | Merkle batch anchoring | **yes** |
| 6 | Tokens (mint/digest/recovery codes) | no — ballot-domain, not shared |
| 7 | The ledger | no — Election Command has its own, see §3 |

### The byte-level proof

`crypto.mjs` exports 23 names. **Every one of them also exists in
`mblts.mjs`. It exports nothing new.**

```
in crypto.mjs only : []
shared             : CANONICAL_VERSION, CLOCK_TOLERANCE_MS, MERKLE_VERSION, b64u,
                     canonicalJSON, concat, ed25519, hex, inclusionProof,
                     merkleLeaf, merkleLevels, merkleNode, merkleRoot,
                     randomBytes, sha256, sha256hex, sha512,
                     timingSafeEqualHex, ulid, unb64u, unhex, utf8,
                     verifyInclusion
```

Comparing each declaration body character-for-character between the two files:

```
IDENTICAL (21): CLOCK_TOLERANCE_MS, b64u, canonicalJSON, concat, ed25519, hex,
                inclusionProof, merkleLeaf, merkleNode, merkleLevels,
                merkleRoot, randomBytes, sha256, sha256hex, sha512,
                timingSafeEqualHex, ulid, unb64u, unhex, utf8, verifyInclusion
DIFFERENT (2):  CANONICAL_VERSION, MERKLE_VERSION
UNRESOLVED:     []
```

SHA-256 of the two largest blocks, taken from each file independently:

```
ed25519       sha256: af7a01fe39c488ae  (ballot-trail)  af7a01fe39c488ae  (election-command)
canonicalJSON sha256: 945adfc3630285ca  (ballot-trail)  945adfc3630285ca  (election-command)
```

**21 of 23 declarations are byte-identical. The two that differ are the two
namespace labels, and nothing else.** The file header, the section prose and the
doc comments were rewritten for this product; the executable bytes were not.

### Ed25519 verified against the official RFC 8032 §7.1 test vectors

Not inherited as a claim — run against **this build's shipped file**,
`/Users/americasfuture/election-command/assets/crypto.mjs`:

```
  ok   RFC 8032 §7.1 TEST 1  pubkey=true signature=true verify=true poison-rejected=true
  ok   RFC 8032 §7.1 TEST 2  pubkey=true signature=true verify=true poison-rejected=true
  ok   RFC 8032 §7.1 TEST 3  pubkey=true signature=true verify=true poison-rejected=true

3/3 RFC 8032 §7.1 vectors passed against assets/crypto.mjs
```

Each vector checks four things: the public key derived from the seed matches the
published key, the signature over the published message matches the published
signature byte for byte, that signature verifies, and — the poison run — a
signature over a **flipped** message is **rejected**. A vector suite that only
checks the happy path is a suite nobody has seen fail.

`ballot-trail/test/engine.mjs` runs the same vectors against the upstream file
(`/* RFC 8032 §7.1 */`, line 15). Both sides pass. That is what makes the copy
safe to hold.

### The verifier is deliberately pure JavaScript

`crypto.mjs` implements Ed25519 in BigInt rather than calling WebCrypto. The
upstream file states the reason and it carries over: WebCrypto's Ed25519 is
uneven across browsers, and *a verifier the reader cannot run is not a verifier.*
The file also states its own limit — it is a reference implementation using
extended coordinates and a plain double-and-add ladder, **not hardened against
physical side-channel attack**, and production keys belong in an HSM. That
caveat is upstream's, and it is repeated here rather than quietly dropped.

---

## 3 · The two re-namespaced constants — and what they actually change

Two constants were re-namespaced when the file was lifted:

| Constant | `ballot-trail` | `election-command` |
|---|---|---|
| `CANONICAL_VERSION` | `MBLTS-CANONICAL-V1` | `BB2G-CANONICAL-V1` |
| `MERKLE_VERSION` | `MBLTS-MERKLE-V1` | `BB2G-MERKLE-V1` |

### Uncomfortable truth: renaming them does NOT, by itself, change any event hash or Merkle root

This must be stated precisely, because the loose version of the claim — *"the
constants were renamed, so the hashes differ"* — is **wrong**, and shipping a
wrong integrity claim in an integrity product is the worst possible defect.

Measured, importing both modules side by side:

```
canonicalJSON  election-command: {"a":"x","b":2,"c":[1,2,null]}
canonicalJSON  ballot-trail    : {"a":"x","b":2,"c":[1,2,null]}
equal: true

merkleRoot     election-command: 9d33f58b8278558c024289455085c05d2037eedf3fb1938c8e8c49a10603f687
merkleRoot     ballot-trail    : 9d33f58b8278558c024289455085c05d2037eedf3fb1938c8e8c49a10603f687
equal: true
```

`canonicalJSON` does not embed `CANONICAL_VERSION`; it serialises the value it is
given. `merkleLeaf` / `merkleNode` use fixed domain bytes `0x00` / `0x01`, not
the version string. **Both constants are documentary labels at the primitive
layer.**

### Where the labels DO bind: the checkpoint signature

`lockchain.mjs` puts both labels **inside the checkpoint preimage**
(`checkpointPreimage`, lines 437–456), and the checkpoint preimage is what gets
signed. So the labels achieve domain separation exactly one level up — at the
signature, not at the hash:

```
{"canonical_version":"BB2G-CANONICAL-V1","checkpoint_id":"c1",
 "checkpoint_version":"BB2G-LOCKCHAIN-CHECKPOINT-V1","committed_at":"2026-08-15T12:00:00.000Z",
 "election_id":"e","event_count":3,"first_event_hash":"sha256:aa","jurisdiction_id":"j",
 "last_event_hash":"sha256:bb","merkle_root":"sha256:00","merkle_version":"BB2G-MERKLE-V1",
 "previous_checkpoint_id":null,"schema_version":"1.0","sequence_first":1,"sequence_last":3,
 "signature_key_id":"k1","tenant_id":"t"}

sha256(preimage)                                   = 3a0f2218…253b74a3
sha256(same checkpoint under the MBLTS labels)     = c6308f9b…ee3b3684
```

A checkpoint signed under `BB2G-*` will not verify as an `MBLTS-*` checkpoint and
vice versa. **That** is the real separation, and it is a signature-domain
separation, not a hash-input separation.

### The roots are not interchangeable for a second, larger reason

Even with identical primitives, an Election Command event hash and a Ballot Trail
event hash are computed over **different field sets**:

- `lockchain.mjs` defines 24 `EVENT_FIELDS`, of which **21 are hashed**:
  `event_id, event_type, schema_version, aggregate_type, aggregate_id, tenant_id,
  jurisdiction_id, election_id, location_id, actor_id, actor_role_snapshot,
  device_id, occurred_at, recorded_at, received_at, payload_hash,
  attachment_manifest_hash, previous_event_hash, signature_key_id,
  source_reference, corrects_event_id`.
- `mblts.mjs` has its own, different event shape for mail-ballot lifecycle
  tokens, and does not export a `HASHED_FIELDS` list at all.

Two different preimages over two different schemas produce two different hashes,
and therefore two different roots — regardless of any label. **The two systems'
roots are deliberately not interchangeable, and they should never be presented as
comparable.**

### Consequence to hold on to

Because the primitive layer is byte-identical, an **independent verifier written
against either file will verify the other's primitive-layer output.** That is a
feature (one audited implementation, two products) and a hazard (a reader could
mistake a Ballot Trail root for an Election Command root). The checkpoint labels
exist so that the *signed* artefacts cannot be confused. Nothing prevents a raw
Merkle root from being confused, so a raw root must never be published without
its signed checkpoint.

---

## 4 · What is NOT reconciled

### 4a · Three trees on this machine claim the name "Election Command"

Measured 2026-08-15 08:29 EDT:

| Tree | Files | Commits | Last commit message |
|---|---:|---:|---|
| `~/election-command` (this build) | 32 | **0** | — |
| `~/bb2g-election-command` | 295 | 1 | "BB2G Election Command — one adaptive interface over 70 election modules" (08:26 today) |
| `~/bb2g-election-os` | 23 | 0 | not a git repository |

`~/bb2g-election-command` is a separate `src/` + `build.mjs` + `dist/` tree. A
machine-wide search finds `lockchain.mjs` in **exactly one** place:

```
$ find ~ -maxdepth 4 -name 'lockchain.mjs' -not -path '*/node_modules/*'
/Users/americasfuture/election-command/assets/lockchain.mjs
```

So the LockChain spine described in this document exists in this tree and nowhere
else on disk. **No merge between the three trees has been performed, and no
determination has been made about which is canonical for the product as a
whole.** That determination is the blueprint's own first task and it remains
open. Recording it is the honest thing to do; claiming it is closed would be the
dishonest thing.

### 4b · Six older election trees, unmapped

`election-integrity`, `safe-vote`, `strongroom-elections`, `verifiable-elections`,
`verified-vote`, `lockchain-record` — enumerated in
`CURRENT_SYSTEM_INVENTORY.md` §2, **read by none of the work in this build**. No
characterization tests were written against them. Whether any of them already
implements a module in the registry is unknown.

### 4c · `@bbg/ledger`

The blueprint names `@bbg/ledger` as a shared platform component visible in the
public BB2G University [2]. It was not found on disk during this pass and was not
compared against `assets/lockchain.mjs`. **Until that comparison is done, this
build cannot claim that `lockchain.mjs` is not itself a duplicate of an existing
shared component** — which would be the very fault the blueprint warns about,
committed one layer up from where it was avoided.

The crypto layer is proved non-duplicate. The ledger layer is not.

### 4d · Remote branches

No `git ls-remote` was run against any `kristenslab/*` repository. Every commit
count in this document is a *local* count. A newer canonical branch may exist
remotely for any of the nine trees.

---

## 5 · Rules that follow from this

1. **`ballot-trail/assets/mblts.mjs` is upstream for the primitives.** A change
   to Ed25519, CanonicalJSON, Merkle or ULID belongs there first, and then comes
   back here as a re-lift. It must not be edited here in isolation.
2. **A provenance gate is owed.** `crypto.mjs`'s own header says
   `test/gate-provenance.mjs` re-reads the source engine and fails the build on
   drift. **That file does not exist in this repository.** The comparison in §2
   was run by hand for this document. Until it is a gate, drift will not be
   caught automatically. This is a stated capability the code does not currently
   have, and it is written down here rather than glossed over.
3. **Never publish a bare Merkle root.** Publish the signed checkpoint, which
   carries the domain labels, the event count and the sequence range.
4. **"Tamper-evident", not "immutable."** `describeLimits()` in `lockchain.mjs`
   ships the caveat to the reader, and `test/engine.mjs` asserts the word
   "immutable" never reaches a user-facing string
   (`ok describeLimits refuses the word "immutable" and states the truth
   boundary`). That doctrine applies to every document that describes this
   engine, including this one.

---

## Sources cited

Cited as the blueprint cites them; no legal claim is added here.

- [1] The Standard — Born Between 2 Generals LLC — https://standard.bornbetween2generals.com/
- [2] Born Between 2 Generals University — https://university.bornbetween2generals.com/bbg-university.html
- RFC 8032, *Edwards-Curve Digital Signature Algorithm (EdDSA)*, §7.1 test vectors.
