podman-compose does not substitute defaults inside "environment:" (a value
like ${MOSHIRO_NAME:-} reached the node as the literal text), so the node was
enrolled with an invalid name and reported the wrong network scope. The three
compose files now read .env with env_file and only interpolate image and
network_mode; the Backend .env keys are the container's own names
(MoShiro__PublicUrl, MoShiro__ClaimCode).
- The generated .env no longer carries a comment line for MOSHIRO_NAME; the
dialog says how to choose a name instead.
- The container entrypoint prints the name it enrolls with and defaults
MOSHIRO_NETWORK_SCOPE to host (Docker Desktop sets container in .env).
- The overview and line pages say "measurement channel not established"
instead of "not connected": the agent can be reachable and pingable while
the measurement TCP/UDP path is not.
Agent, Probe and Backend now start from one static compose.yaml plus a local
.env: the panel (or the release assets, for the very first Backend) provides
the file, the panel's command writes the public values into .env, checks the
file's SHA-256 and runs compose up. Updating is compose pull && compose up -d.
The image is chosen by the CI channel (:latest, or MOSHIRO_VERSION in .env),
not by the panel; the previous one-line script stays as a fallback.
- packaging/compose/{agent,probe,backend}/compose.yaml are the single source,
embedded in the Backend and served at /install/compose/{file}; the root
compose.yaml moved there. Releases also carry them and list them in the
signed SHA256SUMS.
- The container entrypoint defaults the node name to the host name.
- Join token creation returns a compose block (.env, url, hash, per-platform
commands); the admin dialog leads with it.
- Remove code with no references (HotLayer members, LiveHub.SubscriberCount,
QueryService.ParseParams, ReleaseService.Forget, a few constants) and reduce
the install-code lookup to the role it is for.
- Group docs by category with an index (docs/README.md); fix every link,
code comment and the protocol conformance test that read docs/protocol.md.
- access.md and security.md rewritten to what is implemented: removed the
unadopted member-proof chain, roles and recovery key that do not exist,
the old implementation order and the budgets; gaps now carry a status.
- enrollment, deployment, api: the install flow is the static, hash-checked
script with environment variables; no default admin.
- README and architecture: new topology and update-flow diagrams (operator
passkey, CI signing, registry feed, auth and trust modules).
Registering a manifest with the panel is best effort in CI (an unreachable
panel only warns, the pipeline stays green). The Backend update notice then
never appeared, because the host updater only reads the mailbox the panel
writes. A background poller now fetches the signed manifests of the agent, the
probe and the backend itself from the registry every five minutes, verifies the
release signature and registers them, writing the backend one into the host
updater's mailbox.
The panel now lets the deployer decide which release keys it trusts. The
official CI key is built in and always trusted; an own P-256 public key can be
added (off by default, the private key is rejected) so manifests signed with it
are accepted too. Removing it drops the manifests only it signed. Changes are
audited and need update.confirm.
Admin UI: System > Trust > "Trusted release keys" with a collapsible guide on
generating a key pair and signing a manifest with openssl.
Registration now has one path, POST /api/release/offer (no token, signature
verified). The admin POST /releases route, the release.publish capability and
the ci-release preset are removed.
The hash a node reports about itself cannot be trusted (a swapped binary can
copy the old hash), so the runtime binding only caught files that were replaced
by something that did not lie. Provenance stays where it can be verified: the
signed release manifest (digest) and the signed SHA256SUMS.
- Remove BinaryBindingService, the node_bindings table (schema v15 drops it),
BinaryPolicy, the handshake hash/manifest fields and the admin binary column.
- Drop the per-platform "binaries" list from the release manifest, the hash
scripts and the updater's installed.manifest copy.
- ReleaseService.PublishAsync now verifies the release signature, so the admin
POST /releases route is no longer an unsigned path; tests sign with a test key.
- Docs: section 16 explains why the runtime check is gone.
Join codes need no passkey touch and carry only public values (genesis
fingerprint, owner credentials, installer hashes). Nodes pin the genesis and
owners at enrollment and only follow passkey-signed operator set versions.
The panel's server key is listed in the set; pinned nodes require its
signature and run the control link under AES-256-GCM. Password sessions carry
no capabilities. The updater adopts a next_key from a signed manifest.
Bump version to 0.2.0. [CI SKIP]
Methods, roles and binary channels are declared in one table per plane (node and peer). RpcRouter checks direction, kind and role when handlers are registered, when messages arrive (fleet_protocol_violation, disconnect after repeated violations) and before sending. NodeConnection is split by responsibility.
Agents and Probes exit in order at a configured time (default 04:30 UTC+8, switch and time zone in the admin settings) and the service manager starts them again; a local restart_at or MOSHIRO_RESTART_AT wins. The connect kind (TCP connect and TLS handshake timings) is now a default continuous line template, with a section on the line page.
The status helper runs as root with all capabilities dropped, so it could not write a volume owned by uid 1654 (mktemp: Permission denied) and the volume updater failed on rootful Podman. Add DAC_OVERRIDE only.
ConfigStore.Write only created the config root, so a new volume without update/ made update offers fail with IO_PathNotFound. Create the parent directory on write and ship an empty update/ in the node images.
The musl AotAnywhere output is a fully static executable, which cannot dlopen libssl: the Backend exited with 'No usable version of libssl was found' on start. Use linux-x64/arm64 and the Debian runtime-deps base.
Namespaces, assemblies, solution, binaries, images, systemd units, config paths, environment variables, protocol methods and the Linux user are all renamed. Breaking: existing databases, node identities and installed units must be recreated.
The panel decides which probes link to which agents (probes dial out): an
auto-link setting plus per-pair manual overrides with a link matrix in the
admin, and the hovered overview card is lifted above its neighbours.
The versions page shows each node's update state with update buttons, API
tokens get a release scope that can only register releases (used by CI), and
the CI image and release builds now pass the tag, commit and build time.
Probes connect to the panel directly and get the agent list as desired state.
CI signs the image digest of agent and probe releases, the panel forwards the
manifest, and a host updater verifies it with a local public key, pulls by
digest and rolls back when the new version is unhealthy.
Restore with PublishAot so agent and probe images contain NativeAOT
binaries instead of a launcher that needs a .dll, fail the build when the
binary is too small, add agent and probe compose files, and document the
[CI] marker in AGENTS.md.