You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds docs/design/debug-information-service-blaire.md, a draft design for "Blaire" — a standalone, authenticated web service where MPC nodes publish their (redacted) foreign-chain RPC configurations so the team can inspect them centrally instead of pinging each operator. The doc covers a system-context diagram, an HTTP API sketch, Rust handler signatures, a SQL data model, and an auth/risk section. Docs-only; no code changes. Solves #4077.
Changes:
New design doc with purpose/background, workflow, and a Mermaid system-context diagram
Data model: AuthenticatedUser, NodeId, a ForeignChainConfig row type, plus four SQL tables
Auth section: env-var single user for dev → Okta SSO for users; mTLS (reusing backup-cli) for nodes; risk discussion
Reviewed changes
Per-file summary
File
Description
docs/design/debug-information-service-blaire.md
New draft design doc for the Blaire foreign-chain config debug service
Findings
Blocking (must fix before merge):
docs/design/debug-information-service-blaire.md:191-198 — The struct labelled "The already existing Foreign chain config struct in the MPC repo" is not that struct. The real ForeignChainConfig (crates/node-config/src/foreign_chains.rs:49) is per-chain and holds timeout_sec, max_retries, expected_network_fingerprint, providers: NonEmptyBTreeMap<RpcProviderName, ForeignChainProviderConfig>; the whole-node type is ForeignChainsConfig (foreign_chains.rs:17). What the doc shows (id: i64, node_id: String, config: String) is a Blaire DB row. Because the same name is then used as the wire type in both handler signatures (:122, :152), a reader cannot tell what actually crosses the wire. Give the DB row its own name (NodeConfigReport?) and specify the published payload separately.
docs/design/debug-information-service-blaire.md:74 — "the server never sees the secrets, ensuring that no keys can be leaked in case of a breach" is the doc's central security claim, but no mechanism is given, and the naive implementation leaks. ForeignChainProviderConfig, AuthConfig, and TokenConfig all derive Serialize, and TokenConfig::Val { val } (crates/node-config/src/foreign_chains/auth.rs:43-47) serialises the literal token. Separately, rpc_url is itself frequently credential-bearing — private QuickNode/Alchemy endpoint paths, and for AuthConfig::Path the token placeholder sits inside the URL. Name the redacted payload type and state explicitly what is dropped vs. kept (is rpc_url kept verbatim? host-only? AuthConfig kind without the token?).
docs/design/debug-information-service-blaire.md:11,74 — The repo already made a deliberate decision against this exact pattern, and the doc doesn't engage with it. The doc comment on NodeConfigResponse (crates/node/src/web.rs:76-85) says to omit sensitive sub-configs entirely rather than mirror them through "API-safe duplicate" types that strip individual fields, because "those duplicates require keeping two definitions in sync and a missed update silently leaks data" — which is why /debug/node_config exposes only ForeignChainsProviderCounts. Blaire's premise is that mirror. The design should either argue why an authenticated-recipient mirror changes the calculus, or commit to an allowlist-shaped payload constructed field-by-field with a test that fails when a new secret-bearing field is added upstream. As written, the Background at :11 describes the consequence of that decision without acknowledging the decision.
docs/design/debug-information-service-blaire.md:263-269 vs :277 — Two mutually exclusive node-auth mechanisms are specified without a choice. mTLS reusing the migration-service path (crates/node/src/migration_service/web/client.rs:23-56) authenticates a node by its p2p/TLS key against contract state; if that's the plan, node_credentials/token_hash is dead weight and a new long-lived secret that must be distributed to every operator. Pick one and drop the other.
docs/design/debug-information-service-blaire.md:179 — Node identity is unresolved, and the doc flags this itself with an inline note-to-self ("fix so that other parts in the document use the same names, ie node_id/account_id"). NodeId is a three-field struct, but URLs use {node_id} (:106-108) and the DB uses node_id TEXT (:215), with no encoding specified. The contract already keys per-node foreign-chain data by TLS public key — ForeignChainsConfigs(BTreeMap<Ed25519PublicKey, ForeignChainsConfig>) at crates/near-mpc-contract-interface/src/types/foreign_chain.rs:1378 — so adopting that key gives you the contract join for free and forces the question the /history endpoint depends on: what happens to a node's history when it rotates its TLS key?
docs/design/debug-information-service-blaire.md:246-251 — The audit_log schema cannot record what the diagram at :49 ("Who requested which nodes, and when") and the example row at :242 ("Request Node Initial MPC network API, with a simple test that uses mock transport. #1 config") promise. user_id, event_timestamp, event_type has no target column. Add one (e.g. target_node_id TEXT NULL plus a details/params column), otherwise the audit log can't answer the question it exists for.
docs/design/debug-information-service-blaire.md:83,157,159 — The scope of /api/v1/activity is stated three different ways: the requirement says "Users can see their own request history", the section text says "from their account", and the endpoint line says "list all users actions/requests". Self-scoped vs. org-wide is an access-control decision, not a wording detail. Pick one and say who holds audit:read; the handler at :162-165 takes AuthenticatedUser with no scoping parameter, which implies self-scoped.
Non-blocking (nits, follow-ups, suggestions):
docs/design/debug-information-service-blaire.md:62 — The diagram has MPC --> DB directly, bypassing Blaire and its authentication, which contradicts the workflow at :24-25 ("publish their configurations to Blaire … Blaire will authenticate, validate and then store"). Route it MPC --> BL --> DB.
docs/design/debug-information-service-blaire.md:122,165 — Json(report): ForeignChainConfig isn't valid axum (should be Json<ForeignChainConfig>), and a list endpoint returning Json<AuditEvent> should be Json<Vec<AuditEvent>> (also: no pagination on an unbounded audit log). AuditEvent, AuthenticatedNode, ApiError, and AppState are used but never defined in the data model.
docs/design/debug-information-service-blaire.md:102-109 — The scopes (config:write, nodes:read, config:read, audit:read) are introduced in the summary table and never defined or mapped to Okta groups, nor to the dev-mode single user at :273.
docs/design/debug-information-service-blaire.md:108,145 — Singular /api/v1/node?id=…&id=… next to plural /api/v1/nodes elsewhere. Given :143 says comparison could be done client-side, consider dropping the endpoint; otherwise /api/v1/nodes?id=….
docs/design/debug-information-service-blaire.md:116 — "the adress won't be public, which increases obscurity" reads as if endpoint secrecy contributes to the security posture. It doesn't; the mTLS at :277 is the control. Also worth stating here: publishing must be best-effort — a Blaire outage or a rejected report must never block or fail MPC node startup (retry with backoff, log, continue).
docs/design/debug-information-service-blaire.md:24 — "at startup and on reconfigurations": the node has no config hot-reload today (ConfigFile is read at boot), so "reconfiguration" is a restart. Either say so, or scope the reload work as part of this design.
docs/design/debug-information-service-blaire.md:212-269 — The DDL is SQLite-flavoured (INTEGER PRIMARY KEY AUTOINCREMENT, datetime('now'), TEXT timestamps) but the database is never named. Two gaps worth adding: no index on node_config_reports(node_id, created_at) — the exact access path for both /config (latest) and /history — and no retention/pruning policy, though :137 alludes to removing inactive nodes' configs.
docs/design/debug-information-service-blaire.md:3 — Sibling design docs carry both a status and an issue link (see docs/design/node-operator-metrics.md:3-4). Add the link to Add design doc for new debug information service #4077.
No "Alternatives considered" section. The one worth recording: an authenticated, mTLS-gated debug route on the node itself (the migration-service pattern already does node-side mTLS), which avoids standing up the aggregated datastore that the Risks section at :283 correctly names as the main downside.
docs/design/debug-information-service-blaire.md:283 — Missing trailing newline at EOF; several lines carry trailing whitespace.
No prompt-injection or embedded-instruction attempts found in the diff or PR metadata.
Most comments from the Claude review have been addressed in the latest commit, excluding the third blocking point, which I will leave for discussion if necessary. Also, I have not included the "alternatives considered" point from the non-blocking section.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Solves #4077 by adding a new design doc.