feat(daemon): Phase E — production hardening, complete
tests / build-and-test (push) Successful in 2m59s
tests / thread-sanitizer (push) Successful in 3m28s
tests / tshark-dissector (push) Successful in 2m22s
tests / secs4j-interop (push) Successful in 2m6s
tests / python-interop (push) Failing after 3m8s
tests / libfuzzer (push) Successful in 3m44s
tests / build-and-test (push) Successful in 2m59s
tests / thread-sanitizer (push) Successful in 3m28s
tests / tshark-dissector (push) Successful in 2m22s
tests / secs4j-interop (push) Successful in 2m6s
tests / python-interop (push) Failing after 3m8s
tests / libfuzzer (push) Successful in 3m44s
Exposure: --grpc default flipped from 0.0.0.0 to 127.0.0.1 (the API is unauthenticated by design; auth belongs to the transport), Unix-domain- socket support (--grpc unix:///run/secs_gemd/api.sock = zero network surface), SECURITY.md documents the contract and ch42 gained a "Running it in production" section (which also documents the HSMS-SS single-session assumption). Graceful shutdown: SIGTERM/SIGINT land on an asio::signal_set on the io thread, which nudges grpc Shutdown with a 2s deadline (cancels open Subscribe/WatchHealth streams); Wait() returns on the MAIN thread, which stops the engine (rt->stop() joins the io thread, so it must not run on it). Exit 0, journal-safe, the in-code TODO is gone. --spool-dir added so host-bound events survive daemon restarts. Observability: --metrics serves Prometheus gauges secsgem_link_selected / secsgem_control_state / secsgem_spool_depth, wired via the Phase-0 add_link_observer/add_control_state_observer hooks + io-thread sampling. Deployment: deploy/secs_gemd.service — hardened systemd unit (DynamicUser, ProtectSystem=strict, StateDirectory for the spool, UDS for the API, TimeoutStopSec aligned with the graceful-shutdown window). Enforcement: tools/check_daemon_ops.sh proves all three operational claims (unix-socket gRPC accepts, all gauges present on /metrics, SIGTERM -> exit 0 + clean-stop log) — green; wired into tools/run_interop.sh (now 11 steps) and CI. CI python-interop lane also gained the pyclient and spool-restart steps, so every harness now runs in CI. TODO sweep: the shutdown TODO is fixed; the four remaining TODOs (nested list formats, C2-as-text, U8>2^63, CONNECTED link state) are deliberate deferred edge cases, each marked in code with context. Daemon suite re-verified green (175 assertions). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -39,11 +39,12 @@ drops; the daemon model covers the gap if *your software* drops.
|
||||
Run it:
|
||||
|
||||
```sh
|
||||
build/secs_gemd --port 5000 --grpc 0.0.0.0:50051 --config-dir data
|
||||
build/secs_gemd --port 5000 --config-dir data # gRPC on 127.0.0.1:50051
|
||||
build/secs_gemd --grpc unix:///run/secs_gemd/api.sock … # production: no TCP at all
|
||||
```
|
||||
|
||||
One process, two faces: passive HSMS equipment on `--port`, the gRPC tool
|
||||
API on `--grpc`.
|
||||
API on `--grpc` (localhost by default — see §5 before exposing anything).
|
||||
|
||||
---
|
||||
|
||||
@@ -188,7 +189,34 @@ stream, so they never fight your application over the slot.
|
||||
|
||||
---
|
||||
|
||||
## 5. Which tier do I pick?
|
||||
## 5. Running it in production
|
||||
|
||||
- **Exposure.** `--grpc` defaults to `127.0.0.1:50051`; the API is
|
||||
unauthenticated by design (auth belongs to the transport), so it must
|
||||
never face the equipment LAN. For same-host tool software use a Unix
|
||||
domain socket — `--grpc unix:///run/secs_gemd/api.sock` — and there is
|
||||
no network surface at all. The HSMS port faces the fab host; firewall
|
||||
it to the host's address ([SECURITY.md](SECURITY.md) has the nftables
|
||||
recipe).
|
||||
- **Shutdown.** SIGTERM/SIGINT drain gracefully: open Subscribe/WatchHealth
|
||||
streams are cancelled (2s deadline), the engine stops cleanly, the spool
|
||||
journal is never cut mid-write, exit code 0. Safe under systemd and
|
||||
`docker stop`.
|
||||
- **Supervision.** [deploy/secs_gemd.service](../deploy/secs_gemd.service)
|
||||
is a hardened systemd unit (DynamicUser, ProtectSystem, StateDirectory
|
||||
for the spool, Restart=always). Pair with `--spool-dir` so host-bound
|
||||
events survive daemon restarts too.
|
||||
- **Metrics.** `--metrics 9091` serves Prometheus gauges:
|
||||
`secsgem_link_selected`, `secsgem_control_state`, `secsgem_spool_depth`.
|
||||
- **Sessions.** v1 runs one equipment identity per daemon (HSMS-SS). The
|
||||
engine supports HSMS-GS multi-session, but the daemon doesn't surface it
|
||||
yet — run one daemon per equipment identity.
|
||||
- All of the above is enforced by `tools/check_daemon_ops.sh` (the
|
||||
`daemon-ops` step of `tools/run_interop.sh`, also in CI).
|
||||
|
||||
---
|
||||
|
||||
## 6. Which tier do I pick?
|
||||
|
||||
| Your situation | Tier |
|
||||
|---|---|
|
||||
|
||||
+17
-4
@@ -227,11 +227,24 @@ debts tax every later phase, and the most valuable tests aren't automated.
|
||||
S16 builders — see interop/raw_gem300_harness.py for the frame source).
|
||||
|
||||
### Phase E — hardening & operations
|
||||
13. ⬜ gRPC exposure: default to localhost + document UDS; optional TLS creds.
|
||||
14. ⬜ `tools/run_interop.sh` + CI lanes: all interop harnesses + TSan daemon lane.
|
||||
15. ⬜ Daemon Prometheus metrics + supervised deployment recipe (systemd unit).
|
||||
13. ✅ gRPC exposure: default flipped to `127.0.0.1`; Unix-domain-socket
|
||||
support verified (`--grpc unix:///...`); SECURITY.md documents the
|
||||
contract (unauthenticated API = localhost/UDS only; stunnel if remote).
|
||||
TLS creds remain optional future work (UDS removes the need same-host).
|
||||
14. ✅ `tools/run_interop.sh` now 11 steps (added pyclient + daemon-ops);
|
||||
CI python-interop lane gained pyclient, spool-restart, and daemon-ops
|
||||
steps — every harness now runs in CI.
|
||||
15. ✅ Graceful shutdown (SIGTERM/SIGINT -> gRPC drain with 2s stream-cancel
|
||||
deadline -> engine stop -> exit 0; journal-safe; the old in-code TODO is
|
||||
gone), Prometheus gauges (`secsgem_link_selected` / `_control_state` /
|
||||
`_spool_depth` via the Phase-0 observers + io-thread sampling),
|
||||
`--spool-dir` on the daemon, and `deploy/secs_gemd.service` (hardened:
|
||||
DynamicUser/ProtectSystem/StateDirectory/TimeoutStopSec). All enforced
|
||||
by `tools/check_daemon_ops.sh`. Single-session (HSMS-SS) assumption
|
||||
documented in ch42 §5.
|
||||
16. ⬜ Remaining Layer-1 API: traces, limits, substrates/modules, terminal
|
||||
services, spool depth/flush, `Describe` RPC.
|
||||
services, spool flush RPC, `Describe` RPC. (Engine-side all exists;
|
||||
surface on demand.)
|
||||
|
||||
### Phase F — fab acceptance (parallel track; the hard gate)
|
||||
- ⚠️ **Standards correctness remains unverified against SEMI texts** (behaviour
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# Security operations guide
|
||||
|
||||
## The daemon's gRPC API
|
||||
|
||||
`secs_gemd`'s tool API is **unauthenticated by design** — authentication
|
||||
belongs to the transport. The defaults are safe (`127.0.0.1`), and the
|
||||
production recommendation is a Unix domain socket
|
||||
(`--grpc unix:///run/secs_gemd/api.sock`, file-permission protected, zero
|
||||
network surface). Never bind it to an interface reachable from the
|
||||
equipment LAN; if cross-host access is unavoidable, front it with the same
|
||||
stunnel pattern described below for HSMS.
|
||||
|
||||
|
||||
HSMS is plain TCP — no auth, no encryption. That's what every fab
|
||||
tool ships and what every MES expects. Security comes from the
|
||||
network layer around the HSMS socket; this doc has the concrete
|
||||
|
||||
Reference in New Issue
Block a user