1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 | # ardegazu-id-kit
One identity across the [ardegazu.ro](https://ardegazu.ro) suite.
An identity is a single 32-byte Ed25519 seed (43 chars of base64url — short
enough for a password manager). This package holds everything the suite's apps
share about it:
- **`identity`** — the Ed25519 identity: seed → keypair, session binding
assertions, raw signatures, 4-emoji fingerprints.
- **`xkey`** *(separate entry: `ardegazu-id-kit/xkey`)* — the deterministic
X25519 encryption identity derived from the same seed, signed X-certs, and
HPKE-shaped sealed boxes ("wraps"). Needs `@noble/curves` (peer dependency);
apps that never seal anything skip this entry and the dependency.
- **`profile`** — name, chosen hue, chosen glyph; stable identity-derived
colors (`hueOfPub`) replacing the old per-session peerId colors.
- **`bridge-host` / `bridge-client` / `protocol`** — the identity
bridge. The host runs in a tiny page at `https://ardegazu.ro/id/`; every app
embeds it as a hidden iframe (same-site on `*.ardegazu.ro`, so its storage is
unpartitioned) and syncs through versioned postMessage with compare-and-set.
Apps keep a per-app mirror (`<ns>:id`) so booting never waits on the iframe:
offline PWAs, private windows and apex outages behave exactly like the
pre-bridge suite. The record also carries an `apps` map — one section per
app, keyed by the app's own origin (`bridge.putAppState(state)` writes it,
`lastRecord().apps` / `bridge.appStateOf(key)` read anyone's). Write access
is origin-isolated and read access is suite-wide, so an app can publish a
little directory the rest of the suite can act on.
### What a section is for, and what it costs
A section is **directory metadata** — a small index one app publishes so the
rest of the suite can act on it (which banks you've added, which boards you
follow). It is not a place to keep history: the whole identity lives in one
`localStorage` entry on the apex origin, and that quota is an external
constraint, so the record is hard-capped and sections are budgeted inside it.
| limit | value | what it bounds |
|---|---|---|
| `MAX_RECORD_BYTES` | 131072 | the whole serialized record |
| `MAX_APP_STATE_BYTES` | 16384 | one app's section (hundreds of entries) |
| `RESERVED_CORE_BYTES` | 32768 | headroom sections may never take |
| `MAX_APPS_BYTES` | 98304 | the `apps` map as a whole |
The reserve is what keeps `soc` and the profile writable no matter how many
apps hold sections — before it existed, two apps at the per-entry cap could
saturate the record and conflict every other writer forever.
**Units: UTF-16 code units of the JSON serialization** (`JSON.stringify(x)
.length`), never UTF-8 bytes — one rule for every limit above, and the same
rule the suite's sync layer uses. They agree for ASCII and diverge above
U+007F (a Romanian `ș` is one code unit but two bytes), so measuring the other
way would accept locally what the other end drops.
A refused `putAppState` says why: `putAppStateDetailed(state)` resolves
`{ ok, reason }` with `"cas"` (lost the race — retry), `"key"` (this origin
owns no section), `"size"` (over the per-entry cap), `"full"` (the sections'
budget is spent), or `"noseed"` / `"offline"` decided client-side.
`putAppState(state)` still resolves a plain boolean.
Since v2.0.0 the implementation is ClojureScript
(`src/ardegazu/id/*.cljs`, per the suite's CLJS canon in `dev/docs/CLJS.md`).
Consumers see compiled, deterministic ESM in `dist/` plus hand-authored
`types/*.d.ts` — the wire and storage contracts are unchanged and locked by
golden vectors extracted from the v1 TypeScript implementation
(`test/vectors/`, byte-exact signatures included).
## Consuming
```jsonc
// package.json
"dependencies": { "ardegazu-id-kit": "git+https://git.ardegazu.ro/id-kit.git#v2.0.0" }
```
The package ships compiled ESM (`dist/`) with TypeScript declarations
(`types/`) — no transpile-the-source config needed (v1 consumers can drop
their `optimizeDeps.exclude` / `fs.allow` entries whenever convenient).
```ts
import { IdBridge } from "ardegazu-id-kit";
const bridge = new IdBridge({ ns: NS });
const boot = await bridge.boot();
// boot.identity — Identity | null (null = run identity-less, as always)
// boot.profile — { name, hue, glyph }
// boot.conflict — bridge holds a different identity → show the chooser:
// bridge.adoptBridgeSeed(rec) / bridge.keepLocalSeed(rec)
```
npm records the resolved commit in `package-lock.json`, so builds are pinned
even though the mirror moves. Update by bumping the `#tag`.
### Working on id-kit itself
In the consuming app: `npm install ../id-kit` (a `file:` link), iterate, then
restore the git URL before releasing. Note that on `localhost` the bridge
iframe is cross-site to the apex, so dev sessions see partitioned (empty)
bridge storage — dev identities are per-origin, which is expected.
### Dev identities on localhost
The bridge origin allowlist covers `*.ardegazu.ro` plus localhost ports 4173
and 5173 (the suite's pinned dev/preview ports).
## Releasing
```sh
npm run check # shadow-cljs compile + consumer.ts type smoke
npm run build && npm test # rebuild dist/, vectors + bridge tests against it
deploy/check-dist.sh # committed dist == fresh build, no local paths
git commit … && git tag vX.Y.Z
cd ../git && ./assemble.sh id-kit
```
`dist/` is committed and must be byte-deterministic: exact-pinned
clojurescript + shadow-cljs in `deps.edn`, `scripts/normalize-gensyms.mjs` in
the build, and `deploy/check-dist.sh` as the gate (see `dev/docs/CLJS.md`).
The publish pipeline is the suite's usual one: `deploy/publish-repo.sh` builds
an anonymity-gated, dumb-HTTP-clonable bare mirror that assemble.sh pins into
git.ardegazu.ro. Tags ride along in `info/refs`, which is what lets npm
resolve `#vX.Y.Z` over the IPFS gateway.
## License
MIT.
|