UnENVerse 2026-10-09
Licence: LICENSE replaced by the official Apache-2.0 text (curl from apache.org), NOTICE added, license fields in package.json, vscode-extension, tauri.conf, 4 Cargo.toml, image label.
second rename sweep, Apache-2.0, README and CHANGELOG rewrite, demo server and screenshots, CI hardening
- Licence: LICENSE replaced by the official Apache-2.0 text (curl from apache.org), NOTICE added,
licensefields in package.json, vscode-extension, tauri.conf, 4 Cargo.toml, image label. - Demo + screenshots:
tools/demo/seed.py(fake vault via theunvCLI against a throwaway server: 29 entries across the common providers and kinds, 3 projects, 12 categories, users alice/bob/ci-bot, classes Developers/Operators, permission expressions),tools/demo/shots_demo.py,tools/demo/run.sh(startsunv-demoon :18743, seeds, runs the real app in the viewer container over--network host). Output `Scr - CI:
unv-server/tests/nodes_e2e.rswaits forlast_polled(Windows race),history.rssnapshot test sleeps between saves (timing under load),cargo fmtapplied everywhere (the Ubuntu failure was import order after the rename),scripts/preflight.sh+npm run preflight+.githooks/pre-commitmirror CI's gates. - Verification:
cargo test --workspace707 passed;npm run check1497 passed; fmt/clippy clean for unv-cli. - Nothing pushed; no CI run observed for these changes.
- VPS node binary is still release 0.42.1 (pre-rename strings); protocol domains unchanged so it interoperates. Apply (push) for wg0 still off; decision pending (root agent vs polkit).
- Peer comment lines (
# name) of the live wg0.conf are not produced by the exporter. - The CHANGELOG is machine-compiled from session notes; wording is uneven for older entries.
- VS Code extension settings keys are now
unenverse.*(users must re-set them).
the rename to UnENVerse, the Discord acceptance, restore-into-chunks, and a way to see the app
- New in this round: public inference (
ImportedVar.public). A variable is marked public only when it is plainly not a secret: names that look secret (key, token, secret, password, webhook, sig, auth, passkey) always win; hex ints, bools and floats are public; short ints are public; URLs are public unless they carry userinfo, a credential query parameter, a webhook path or a token-looking path segment; prose strin - History pane: a "Restore into chunks" button on each snapshot row for formats with a reader. It fetches the real text with
reveal: true, confirms, applies, saves, repaints. - Nodes pane: "Read into a chunk" is now offered for those formats as well as
.env, using the same plan and confirmation. - Values arrive as literal text (the deployed file had no references), and the confirmation says so. There is deliberately no Rust twin (six formats parsed twice would be six chances to disagree); no new Tauri command, so the capability map needs no entry. Test:
tests/restore-chunks.test.ts. - Lazy
version_historyloading in the app; concurrent polls for listening nodes; certificate and hub transport key rotation; a Nextcloud rotation adapter; the 24.4 container benchmark (needs a 2 vCPU host); a hardware or passkey approver; Grafana token expiry in the probe. Each needs either a decision or a real service; none is a code gap that tests can close. - A4, A9, A10 smoke tests: the viewer now makes them possible (Xvfb has X11 only; Wayland is still not covered, and Caps Lock and the input method need
xdotooland an IM inside the image).
everything that was left: 30.1/30.2, 34.1, 37.1, 38.1, 29.1, 28.1, 31.3 and the leftovers of 24.5, 35 and 36
- Restore a history snapshot into chunks (35): only
.env-style files have a Rust parser; the other formats' parsers are TypeScript, and a second implementation would be a new twin pair. The pulled-file path (--into-chunk) covers the.envcase. - **Lazy
version_historyin the app (30.2), concurrent polls and certificate rotation for listening nodes (ADR-0145), a hardware key as approver (ADR-0147), a Nextcloud rotation adapter and Grafana token expiry** (ADR-0148).
30.1 (delta saves, server and store half) and 29.1 (two more rules)
| File | Change and why |
|---|---|
src/ts/state.ts | RemoteVaultStore.save now calls private send('PUT', data); new saveRows(put, del) calls send('PATCH', {put, delete}). One body of toast, If-Match and etag logic for both. |
tests/remote-save-rows.test.ts (new) | Stubs fetch: PATCH method, body is exactly the delta, the returned ETag is sent as the next If-Match. |
| vault-core/src/config_check.rs | Two rules, RULES 12 to 14. compose-network-undeclared (error): a service networks entry that no docker_network chunk declares; silent unless the project declares at least one network, default always exists, refs are skipped, names compared case-insensitively. k8s-service-selector-unmatched (warning): the starters generate selector: app: <name>, so a Service selects the
- App dirty tracking. The Tauri side and
state.tsstores still save the whole document; nothing callssaveRowsyet, and no Taurisave_vault_rows/Access::save_rowsexists. The app tracks no dirty ids. This is the larger half of 30.1 and the one that touches every mutation site in the renderer. - Pinned-HTTPS proxy (
remote_request) still returns no headers, so ETag andX-Vault-Mergeddo not reach the store over it. - 30.2 (chunk rows, age-based retention, full-load speed) untouched.
- 29.1:
envv check --all-projectsand a separategate()function remain unbuilt (the push gate already refuses on errors). - 38.1, 34.1, 28.1 per-pane tests, 31.3 further catalogue entries unchanged.
Sub-phases 28.1 and 29.1, the 1.0 viability review, the decision to defer 30.1/30.2
Phase 39: pre-1.0 security review and pentest checklist
Phase 38: stack integrations
- Integrations (Prometheus, Grafana, Homarr/Homepage, Nextcloud, VS Code) each hand-written in four places (Rust exporter, TS exporter, starters, rules) would add a dozen drift points. Phase 38 also had to name "the adapter shape they share".
- The shape: a JSON descriptor per integration (
vault-core/data/stack-adapters.json) with chunk types (fields, secret flags, defaults), an output document grammar (literal,{f,as,fmt}, map, list, each, singleton, group, entry, when) and rules (unique, required, exclusive, together, choices, needs_one_of). Two interpreters:vault-core/src/stack.rsandsrc/ts/stack.ts, pinned by golden files. Adapters shipped: `p
Phase 37: approval
Phase 36: blast radius
- Nothing recorded which secrets a rendered file *contained*.
- Local
envv execand--outare deliberately outside the audit chain, so there was no record of what reached *this* machine. - By the time of a compromise an entry may have been rotated, renamed or deleted, so "what is in the vault now" is the wrong thing to look up.
- The app has no UI for
--host local(by nature: the app's copy actions are not logged). If the app ever logs its own copy/export actions it should write the same ring through a Tauri command. matlog::notebuilds aMatcherper call (O(secrets)); fine for a handful of materialisations per command, wasteful in a loop. Cache the matcher inrememberif a command ever materialises per-item.exposedlists are computed at snapshot time against the vault *then*: an entry created after the file was written is correctly absent; an entry whose value was identical by coincidence is present (reported, marked short if under 8 characters).
Phase 35: the config time machine
Phase 34: Nodes
listenmode; pull-into-chunks; per-beat render cache (every beat loads the vault and renders; fine at tens of nodes, measure before hundreds; the ceiling is O(vault) per beat per node).node.applyrows carry the node's reported error text (cut to 400 characters, scrubbed on the node). A hostile node can still write misleading text into its own audit rows; it cannot forge another node's actor.- The hub trusts a node's
HostInfo/TargetReportfor display only; none of it feeds a decision exceptapply(which only controls whether content is sent, and the node re-checks). - No rate limit on a signed node's beats beyond the per-IP failure limiter (a valid node can beat as fast as it likes). Add one if nodes are ever less trusted than "the operator's own machines".
- Phase 39 (pentest) should cover: enrollment token guessing and the 12-attempt limiter, replay across restart, the signature input format, the config-check gate bypass by project-name tricks, command injection through
reload(only the node's own file feeds it), and the long-poll as a resource-holding route.