feat(pine): node-status endpoint + Claude voice brain + mesh announcements + wake-word groundwork

- GET /api/pine/status: public tier (version/uptime/bitcoin/mesh facts) for
  the Pine page; bearer-token tier (Lightning balances, latest mesh message)
  for the seeded Home Assistant sensors. Token minted 0600 at
  secrets/pine-status-token; nginx location ships via the bootstrap
  self-heal + canonical ISO conf. Replaces the per-node socat forwarder and
  bitcoind RPC credentials living in HA's configuration.yaml.
- pine_ha seeder: REST sensors + four ask-Archy intents (block height,
  peers, sync %, Lightning balance) with LLM tool descriptions; Claude
  conversation agent (anthropic entry from the node's claude-api-key) set as
  pipeline default with prefer_local_intents so exact phrases stay local and
  fuzzy ones tool-route through Claude; mesh-message announce automation on
  every Assist satellite; bounded config markers with legacy-block migration.
- pine-openwakeword joins the pine stack (all lifecycle sites) — on-node
  wake-word engine groundwork for the custom "Yo Archy" model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-07-22 02:59:34 -04:00
co-authored by Claude Fable 5
parent b288be314c
commit e074a117d2
12 changed files with 763 additions and 103 deletions
+20
View File
@@ -580,6 +580,26 @@ impl ApiHandler {
self.handle_app_catalog_proxy().await
}
// Pine node status — public tier (version/uptime/height/sync/peer
// counts) is unauthenticated like /bitcoin-status; Lightning
// balances + latest mesh message additionally require the bearer
// token the pine/HA seeder minted (or a valid session).
(Method::GET, "/api/pine/status") => {
let bearer = headers
.get(hyper::header::AUTHORIZATION)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.strip_prefix("Bearer "))
.unwrap_or("");
let authorized = self.rpc_handler.pine_status_token_ok(bearer).await
|| self.is_authenticated(&headers).await;
let body = self.rpc_handler.pine_status_json(authorized).await;
Ok(build_response(
StatusCode::OK,
"application/json",
hyper::Body::from(serde_json::to_vec(&body).unwrap_or_default()),
))
}
// LND connect info — nginx validates session cookie (presence check),
// backend is bound to 127.0.0.1 so only nginx can reach it.
// No backend auth check here because the LND UI iframe fetches this