feat: M2 — management REST API with OpenAPI doc (control-pane groundwork)

A versioned control-plane REST API (/api/v1) on its own port (default
127.0.0.1:47990) serving host info, runtime status, paired-client
management, the pairing PIN flow, and session control (stop / force-IDR).
The OpenAPI 3.1 document is generated from the handlers by utoipa, served
live at /api/v1/openapi.json (+ Scalar docs at /api/docs), printable via
`lumen-host openapi`, and checked in at docs/api/openapi.json for client
codegen — a test fails if it drifts, mirroring the cbindgen header rule.

Auth: optional bearer token (--mgmt-token / LUMEN_MGMT_TOKEN), enforced on
everything but /health, and mandatory for non-loopback binds. PinGate
gains a waiter count so the API can report pin_pending; logs moved to
stderr so stdout stays machine-readable. Supersedes the web.rs stub.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 21:35:43 +00:00
parent 22a982a1cb
commit a339a0466e
10 changed files with 1862 additions and 49 deletions
+11
View File
@@ -42,6 +42,11 @@ impl ServerIdentity {
(c, k)
}
};
Self::from_pems(cert_pem, key_pem)
}
/// Build an identity from PEMs (no I/O).
pub fn from_pems(cert_pem: String, key_pem: String) -> Result<ServerIdentity> {
let priv_key = RsaPrivateKey::from_pkcs8_pem(&key_pem).context("parse host private key")?;
let signing_key = SigningKey::<Sha256>::new(priv_key);
let signature = cert_signature(&cert_pem)?;
@@ -52,6 +57,12 @@ impl ServerIdentity {
signing_key,
})
}
/// Throwaway in-memory identity — nothing touches the config dir (used by tests).
pub fn ephemeral() -> Result<ServerIdentity> {
let (cert_pem, key_pem) = generate()?;
Self::from_pems(cert_pem, key_pem)
}
}
fn generate() -> Result<(String, String)> {