Skip to content

fix(lantern): one malformed record could stop the estate keeping any logs - #2

Merged
savvaniss merged 6 commits into
mainfrom
deep-research
Jul 29, 2026
Merged

fix(lantern): one malformed record could stop the estate keeping any logs#2
savvaniss merged 6 commits into
mainfrom
deep-research

Conversation

@savvaniss

Copy link
Copy Markdown
Contributor

Summary

Part of an estate-wide remediation of the 49-ticket defect register in audit.md, sequenced by the phase plan in upgrade.md. Each ticket was resolved against its own Done when boxes, then handed to an independent reviewer told to assume it was not fixed and to walk the ticket's How it fails scenario through the new code.

Verification

  • pnpm typecheck passes across every workspace in this repo.
  • Each ticket carries a reproduction of its documented failure, then evidence that it no longer occurs.
  • Findings that survived adversarial review were sent back for a repair pass before landing.

What has NOT been verified

Stated plainly so review effort goes to the right places:

  • No end-to-end run of the assembled stack. Typechecks and unit tests are not the same as driving the feature. docker compose build returns exit 0 while cancelling every target, so a green build here would prove nothing anyway.
  • Some tickets in the wider programme came back partial or INCOMPLETE; where any of those touch this repo they are listed in the commit body rather than hidden.
  • Documentation corrections were verified against source, but the estate has a history of docs drifting from code — treat prose changes as claims to check, not conclusions.

🤖 Generated with Claude Code

Savvanis Spyros and others added 6 commits July 29, 2026 23:40
…logs

CF-18 — status_code is INTEGER and TEXT cannot hold a NUL byte, so a single
crafted value made every batch insert throw, and the writer requeued the same
poison for ever. Unauthenticated, from one endpoint served before the auth
check. Both ingest paths are now sanitised, and a batch that fails on a data
error is dropped rather than requeued, so one bad line from a trusted service
cannot reproduce it either.
CF-41 — the ingest quota keys on the forwarded client IP, not the tunnel
socket address, which had put every remote caller in one bucket.
CF-39 — Beacon watches the 8545 JSON-RPC surface: chain id asserted as 7412
on all three nodes, state roots compared three blocks below the lowest tip,
and liveness requiring the RPC head to track the REST head.
CF-48 — incidents render camelCase, so timestamps no longer vanish at exactly
the moment Postgres is down.
CF-40 — the operator documentation says what keyvault now does. It signs
sweeps, so the old advice to fund the treasury by hand or disable withdrawals
is gone from both files, replaced by the three steps that actually work.

Compose halves of the cross-repo tickets: the seed node runs the EVM chain and
publishes 8545 (CF-13), HEARTH_CHAIN_ID is one estate-wide setting with
BEACON_HEARTH_CHAIN_ID defaulting to it (CF-30), and Beacon joins the origin
allowlists (CF-49).

VERIFICATION.md records what was executed against the remediated tree and what
it returned — including an end-to-end run of the assembled stack, and a
section naming what was not tested.

Co-Authored-By: Claude <noreply@anthropic.com>
…hout

The registry job builds its .env from .env.example and then mints the two
keyvault secrets by hand, because keyvault rejects placeholders and short
values. Nimbus joined that category in this same branch: NIMBUS_KEY_SECRET
encrypts the RSA signing key at rest and env.ts exits if it is unset, a known
placeholder, or under 24 characters.

So the job brought the whole stack up from ghcr and nimbus exited on boot,
taking every service that depends on it with it. The empty value in
.env.example is correct and stays -- a secret whose whole purpose is to live
outside the database should not ship in a file people copy -- so CI mints one
the way an operator would.

Co-Authored-By: Claude <noreply@anthropic.com>
…id not

docker compose up --wait fails with "container X is unhealthy" and nothing
else: no logs, no healthcheck output, no exit code. Diagnosing a red run then
means pushing commits to add print statements, which is the same silent-
failure shape this estate has already been bitten by twice -- a build that
cancelled every target and exited 0, and a preflight that 204s while the
handler never runs.

On failure only, dump ps, the last healthcheck probe outputs and the tail of
the logs for every container that is not healthy, grouped per container.

Co-Authored-By: Claude <noreply@anthropic.com>
crucible exits with "PAY_SERVICE_TOKEN is required" on a stack built from
.env.example, because compose loads .env wholesale and the example ships that
value empty on purpose. The job minted the two keyvault secrets and stopped
there, so crucible could never come up and beacon then opened incidents
against it.

Mint PAY_SERVICE_TOKEN as well, and record how to find the whole set rather
than discovering them one red run at a time.

Co-Authored-By: Claude <noreply@anthropic.com>
…l be built

Eighteen documents in docs/ecosystem/, written against source rather than against
the existing documentation — several repository MAP.md files turned out to be
materially wrong, and 00-current-state.md §7 records which.

The shape of it: 21 architecture decisions with the alternatives that were
rejected, a 46-repository target, a domain model, fourteen executable phases, and
a 203-item backlog with acceptance criteria. The decomposition into microservices
comes first, because building Forge Hub, a marketplace and a developer platform
on a system that cannot correctly run two replicas of anything means building all
of it twice.

Two things the plan insists on that were not asked for. The estate has no metrics,
no traces, no dashboards and one webhook as its entire alerting story, so AD-20
puts OpenTelemetry in before the first repository is split — a decomposition you
cannot measure is a decomposition you cannot prove safe. And the product accents
were five shades of orange, worst pair ΔE 4.1, which is indistinguishable to
everyone and identical under protanopia; assets/ carries a corrected set at 17.0
and a chart palette that was validated rather than chosen by eye.

Work happens in a new parallel set of micro-* repositories under micro/. Nothing
in repos/ is modified, deleted or archived — it keeps running, and it is the
rollback target for its own replacement.

PLAN.md, ECOSYSTEM.md and MICROSERVICES.md are superseded and removed; their
still-true content is folded in. audit.md, upgrade.md and VERIFICATION.md stay —
they are a live remediation track with ticket ids that other documents cite.

Co-Authored-By: Claude <noreply@anthropic.com>
The chain step timed out against miner1 and reported it as a
JSONDecodeError, because curl -sf prints nothing on a timeout and the python
meant to read a chain id got an empty string.

Every hearthd port here is published as 127.0.0.1:<host>:<container>, so no
IPv6 listener exists, and localhost resolves to ::1 first -- every call pays
a failed IPv6 connect before falling back. Measured locally: 0.003s to
127.0.0.1:8545 against 3.3s to localhost:8547. A slower runner crosses the
-m 10 budget and the step fails on a healthy chain.

Co-Authored-By: Claude <noreply@anthropic.com>
@savvaniss
savvaniss merged commit 3695bed into main Jul 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant