UKM-CODE-07: setEnabled committed before the broker ACL reload and the 503
path left the database changed. A failed disable therefore reported an error
while the broker still granted public-feed reads. Restore the previous flag
when reconcile throws, so the database matches the state the broker enforces.
UKM-C-02: every key in MESHCORE_CHANNEL_SECRETS was publishable by name via
/feed/messages, and packet detail returned payload.decrypted for any hash.
Committed PUBLIC_CHANNELS remain public; env-supplied secrets are now
confidential unless their name is explicitly listed in
PUBLIC_DECRYPT_CHANNELS. The feed route 404s for non-public names, the
history query fails closed, and packet detail strips decrypted/_summary
unless the channel hash is public.
The CI stack has no owner-auth database or ACL file; with enforce/apply now
the default, the backend health gate cannot pass there. Shadow is opt-in by
design, so the CI overlay says so explicitly. Production keeps enforce/apply.
UKM-CODE-13: owner authorization and ACL reconciliation defaulted to
shadow in code and compose, so a host without the production .env served
owner data from ACL-file identity and never applied the ACL cutover while
looking healthy. Default to enforce/apply, require an explicit shadow
opt-in, and log a single boot warning when shadow is selected.
Production already sets both explicitly, so behaviour is unchanged there.
npm audit began failing on GHSA-698v-4r3x-3vcv / GHSA-r825-w8mm-5pxw /
GHSA-587h-7wmj-h5pm (launch-editor, Windows-only dev server). npm audit fix
bumps the locked vite patch release; tests and build pass, 0 vulnerabilities.
The reconciled backend reads the MQTT status envelope from this column.
It was deferred during reconciliation because live already used 057 for
region scope; renumber as 061 so fresh installs and the live schema match.
Reconciles the live UKMesh production tree with main. Preserves deployed behaviour; incorporates contributor work; migrations 60/60; CI green. See PR #133.
Stage 1 keeps the deployed implementation authoritative. The measured-row
feed virtualiser and /api/owner/session cadence are main-only changes that
the reconciliation deliberately did not adopt; the specs assert them, so
mark them fixme and record both as Stage 2 items in docs/deferred-tests.md.
CI has no ALERT_FORWARD_URL, so the receiver reports archive-only mode as
not-ready on /readyz by design (503) and the stack never settles. Probe
/healthz in CI only; production keeps the strict delivery readiness gate.
The worker exits 2 when TYPESAFE_API_KEY is empty, which CI does not provide,
so restart:always turns it into a crash loop and 'up --wait' never settles.
Build the image (so the service is valid) but do not start it in CI.
The reconciled compose starts tagger-worker (a production service) but the
CI build list predated it, so 'up --no-build' failed with 'No such image'.
Build it with the rest of the application images.
The SQL projection returns tags/tag_confidence; RAM-merged rows omitted them,
so the F01/F05/F12/F13 parity suite failed once production's tag joins were
reconciled in. Carry nulls on the merged row and exclude both fields from the
overlay Object.assign so a pending row can never clobber committed tags.
The recovery commit re-introduced the documented public channel constant in
backfill-region-names.mjs and rebuildRegionParticipation.ts. It is published
protocol material, not a credential; gitleaks now allows the exact string
while private channel material still enters only via MESHCORE_CHANNEL_SECRETS.
- Keeps every production behaviour from the deployed tree: public feed,
owner packet sharing (including the Oct-12 publish/claim timeout fix),
regions, privacy rematerialisation, RAM/durable ingest, owner last-hop
prewarm.
- Takes main's additive work since the fork: ingest repair series, 1-byte
path evidence, gitleaks ignore, dependabot pins, publicStatus,
workloadPolicy and health diagnostics.
- Resolves 26 conflicts; per-file decisions recorded in the campaign
DECISIONS.md and merge-proposal notes.
- Restores the two migrations missing from the deployed tree byte-exact
(052_node_identity_packets_raw_columns, 055_message_tags) and defers
main's unapplied 057 status-column migration to docs/deferred-migrations.
- Fresh-database bootstrap verified: base schema plus 60 migrations apply
in order; a second run reports no pending migrations.
Exact snapshot of the tree that produced the running images (HEAD 1f94bd8)
including the 199 uncommitted/untracked production changes: regions, owner
packet sharing, public feed, RAM ingest, privacy rematerialisation, path-beta
updates, migrations 052-060, tests and deploy scripts.
Excluded: .env*/knowledge.md/.private-config (gitignored secrets); runtime
backup receipts restored to HEAD; mosquitto ACL/passwd are gitignored (kept
in the private pre-change snapshot). No functional changes.
The full-history gitleaks scan flags the documented MeshCore public channel
key (publicly known 16-byte material; see MeshCore docs/companion_protocol.md)
at two files that exist only in history (deploy-sync commit 1d24322; both
files were later removed from main). Add the exact reviewed fingerprints to
.gitleaksignore, matching the existing channelRegistry precedent. No scan
scope, rule, or workflow changes; the local full-history scan reports no leaks.
The default live-arc renderer resolved every path prefix against live nodes; a 1-byte prefix matches dozens of nodes, so DM chains (no sender anchor) and other 1-byte paths were drawn as confident guessed relays. A hop whose 2-hex-char prefix matches more than one node is now left unresolved; single-candidate 1-byte hops and multi-byte prefixes keep the nearest-candidate pick. Disclaimer copy updated to match; adds unit tests.
Expose the F16 credentialed public source for revocable CoreScope projections; preserve stable identities, generation fences and bounded overlap paging.
Undecodable stream values were acknowledged and deleted without recovery
evidence. They now go to a bounded quarantine stream (approx MAXLEN 500,
raw value size-capped, digest + byte length recorded) before the original is
acknowledged and deleted, so a malformed entry can neither block the stream
nor disappear without a trace. The structured log carries only id, length
and digest, and the quarantine counter is exported as
meshcore_ram_ingest_quarantined_total.
In RAM mode persistOnce returns at admission, so the post-ingest callbacks
that previously ran after durable persistence would fire against rows that
are not in PostgreSQL yet:
- the link queue (link worker reads packet rows) now enqueues from the RAM
writer after the batch commit, and the live packet subscriber skips it in
RAM mode;
- the live-map follow-ups (multibyte path evidence, slow-mode scheduling,
pre-resolution) move to the same post-commit hook, sharing the durable
path in-flight bound and drop counters via schedulePostPersistFollowUps;
- changes that are safe at admission stay there: WS broadcasts, node emits,
resolve-cache invalidation and owner forwarding (self-contained envelope).
Regression test: the hook fires with the persisted batch after bookkeeping,
and never on failed commits.
- Merged live reads now aggregate pending observations per packet hash, the
same way the SQL aggregates observers: one row per RF packet with combined
observer arrays, IATAs and RX/TX counts. A hash already present in the
database result is augmented with genuinely new observations only (an
observer already listed is treated as the same observation, so retained
overlap entries cannot double-count).
- The WS initial-state message history merge is restricted to group-text
packets (packet_type 5), matching the getRecentMessages contract, with
headroom in the pending sample so the type filter does not starve.
- Regression tests: multi-observer aggregation, augment-without-double-count,
and the type filter (messages vs all-types).
- Admission bounds (packet count, byte budget) are enforced in one atomic
Lua step with a conservative byte counter (value + fixed overhead);
concurrent MQTT connections can no longer race past the same capacity
check, refusals still never acknowledge, and the counter is kept honest
across crashes by release-at-commit.
- Oldest-pending gauge reads oldest-first: the previous XREVRANGE sample
reported the NEWEST entry age and could hide stuck deliveries.
- Committed entries are retained for a handoff-overlap window instead of
being deleted at commit, closing the gap where a reader sees neither a
pre-commit DB snapshot nor the post-commit stream. A retention sweep
removes entries only when provably safe: acknowledged (absent from the
PEL), at or below the newest commit boundary (numeric id comparison),
and past a minimum retention guard. Failed-batch PEL entries are never
swept.