archipelagoandClaude Opus 5 40b77e392a fix(10-03): don't bless a cert minted under an untrustworthy clock
The failure fail-closed cannot catch, because generation SUCCEEDS.

This unit runs very early (DefaultDependencies=no, Before=ssh/nginx), long
before time has synced. `openssl req -x509` stamps notBefore from whatever the
clock says, so on a node with a dead RTC or a flat CMOS battery the cert can be
years out: clock ahead -> clients reject it as "not yet valid", a harder failure
than the usual self-signed warning; clock behind -> notAfter is already in the
past once time syncs. The completion marker was then set and never revisited —
a node permanently serving a cert nothing accepts.

Finding 1, reported rather than assumed: this image does NOT use
systemd-timesyncd. It installs and enables chrony, and chrony-wait.service —
the unit that is Before=time-sync.target — is not enabled. So time-sync.target
is inert here and ordering After= it would buy nothing. Enabling chrony-wait to
make it meaningful would stall boot behind NTP on a node with no network, and
these nodes are routinely offline at first boot. Not deadlocking boot outranks
cert-date elegance, so the ordering is deliberately left alone.

Fixed locally instead, in two parts:

1. Backdate notBefore by 24h so ordinary skew between node and client cannot
   invalidate a fresh cert. -not_before/-not_after arrived in OpenSSL 3.5 and
   the rootfs is debian:trixie which ships it, but the capability is PROBED,
   not assumed — guessing wrong would fail every attempt and brick the node,
   the exact outcome all of this exists to prevent. Without the flags we simply
   do not backdate and rule 2 still covers the dangerous case.

2. Refuse to bless a cert dated by a clock outside a plausible window
   (2026-01-01 .. 2056-01-01). The material stays installed so the node is
   usable and sshd comes up, but the bad dates are recorded as
   failed=cert-dates and the cert is regenerated automatically once time syncs.

Generation is now driven by need rather than by "is the marker absent", and
ConditionPathExists=! is removed from the unit so a node that already completed
can still be re-examined — skipping the unit is precisely how such a node stays
broken forever. The script exits in milliseconds when everything is fine.

Anti-spin is one condition: a date-driven regeneration happens ONLY when the
clock is currently plausible. A node whose clock is still wrong re-checks and
mints nothing.

Regression caught while writing this: driving generation purely by content made
needs_ssh() false whenever any host key existed, which would have left an
image-baked fleet-shared key in place forever — F-03 reopened. The marker check
is back in both needs_ functions and case 1 (which prestages a baked key and
asserts it was replaced) is what caught it.

Case 8 covers mint-under-wrong-clock, repair-after-sync, and both spin
directions. Controls: blessing regardless of clock reddens only case 8
(run1-BAD-DATES-NOT-RECORDED); removing the anti-spin guard reddens only case 8
(SPINNING-reminted-while-clock-still-wrong(1->2)). The second control initially
passed against a broken guard because the assertion compared certificate dates,
and a re-mint under a frozen clock produces a byte-identical notBefore — the
assertion now counts mints, which is the only thing that distinguishes "left
alone" from "regenerated again".

Not covered here: nodes already deployed from earlier ISOs never receive this
script (it is installed by the installer, not by OTA), so fleet remediation for
them remains 10-04/OTA work in core/**, which is held by other executors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 10:08:17 -04:00
2026-03-17 00:03:08 +00:00

Archipelago

Self-sovereign Bitcoin node OS and manifest-driven app platform.

Archipelago is a bootable personal server OS for Bitcoin infrastructure, self-hosted apps, mesh communication, decentralized identity, and federation. Apps are packaged as declarative manifest.yml files and run as rootless Podman containers managed by the Rust backend.

Debian 13 License Rust Vue.js Version

What is here

  • core/ - Rust workspace: backend API, container runtime, security, OpenWrt helpers, and performance/resource management.
  • neode-ui/ - Vue 3 + TypeScript frontend.
  • apps/ - app manifests and custom app container sources.
  • docker/ - supporting container build contexts for UI companion surfaces.
  • image-recipe/ - bootable image/ISO build inputs.
  • Android/ - Android companion app.
  • scripts/ - development, release, deployment, and validation tooling.
  • docs/ - architecture, app packaging, operations, API, and roadmap docs.

Platform model

Archipelago is built as a developer-ready app platform, not a fixed appliance:

  • Apps are declared in apps/<app-id>/manifest.yml.
  • The Rust parser in core/container/src/manifest.rs is the canonical schema.
  • The orchestrator compiles manifests to rootless Podman/Quadlet runtime state.
  • App data lives under /var/lib/archipelago/<app-id>/.
  • Secrets are generated or read from /var/lib/archipelago/secrets/ and injected through Podman secrets rather than static environment values.
  • Release and app catalogs are signed and verified against a pinned trust anchor.

Start with:

Quick start

Frontend

cd neode-ui
npm install
npm start

The dev UI runs at http://localhost:8100 with a mock backend on :5959.

Backend

cd core
cargo build
cargo test --all-features

Linux is the supported backend runtime and release-build target. macOS is fine for frontend work and many Rust compile/test loops, but host integration tests that touch Podman, systemd, networking, or image build paths require Linux.

App manifests

./scripts/validate-app-manifest.sh apps/filebrowser/manifest.yml
python3 scripts/generate-app-catalog.py
python3 scripts/check-app-catalog-drift.py --release --strict

scripts/generate-app-catalog.py requires Python with PyYAML installed.

Documentation map

Doc Purpose
Architecture System layers, crates, data paths, security model
Developer Guide Local setup, code workflow, testing
API Reference JSON-RPC API overview
App Developer Guide How to package and test apps
App Manifest Spec Manifest schema and validation rules
Nostr Git Source Hosting Plan ngit/NIP-34 contribution workflow and maintainer model
Apps README Packaged app catalog overview
Image Recipe Bootable image build flow
Operations Runbook Production operations and recovery
Open Source Readiness Public-release cleanup checklist
Roadmap Shipped, in-progress, and planned work
Unified Task Tracker Launch hardening task list
Archive Historical plans, audits, and handoffs

Contributing

Read CONTRIBUTING.md before opening a pull request. For security issues, follow SECURITY.md and do not open a public issue.

License

Archipelago is licensed under the MIT License. Third-party notices are listed in NOTICE and generated license inventories in component release artifacts.

S
Description
No description provided
Readme MIT
3 GiB
2026-07-24 08:06:36 +00:00
Languages
Rust 42.1%
Vue 21.6%
TypeScript 15.2%
Shell 8.1%
JavaScript 5.5%
Other 7.5%