Skip to content

init_edge_runtime must TAKE the node keystore alias — the mirrored rule forked in server 0.5.195 #548

Description

@emooreatx

What happens

A first-run agent node provisions its node key successfully and then edge refuses to open it, because the two halves derive its name independently and disagree:

alias the agent passes    ciris-agent-bootstrap
server provisioning mints ciris-node-bootstrap-peulxofzaj   ← fixed base alias
edge tries to open        ciris-agent-bootstrap-node        ← alias + "-node"

Disk confirms which one exists:

identity/ciris-node-bootstrap.ed25519.seed.blob      present
identity/ciris-agent-bootstrap-node.*                absent

Edge's refusal is correct — it is #541 declining to fall back to the agency-bearing key. The defect is that provisioning succeeded and put the key somewhere edge will never look. Tracked from the server side as CIRISAI/CIRISServer#511.

Why it is an identity split, not a missing file

fedcode::derive_key_id(alias, ed_pub) takes the alias as an input. The same key material under two aliases is two key_ids, which is two identities. This is not a lookup miss that a fallback could paper over.

Where the two rules live

Edge (src/ffi/pyo3.rs):

// :4146
const NODE_ALIAS_SUFFIX: &str = "-node";

// :4152
fn node_alias(keystore_alias: &str) -> String {
    if keystore_alias.ends_with(NODE_ALIAS_SUFFIX) { return keystore_alias.to_owned(); }
    format!("{keystore_alias}{NODE_ALIAS_SUFFIX}")
}

// :4240 — open_node_identity_halves
let alias = node_alias(host_keystore_alias);

Server (src/key_convention.rs, 0.5.195+): one fixed literal, ciris-node-bootstrap, for every node. Deployment identity comes from the fingerprint the federation appends, not from the alias.

Why the existing guard could not catch it

node_alias_matches_the_server_rule (src/ffi/pyo3.rs:11250) asserts edge's copy against a hardcoded literal:

assert_eq!(NODE_ALIAS_SUFFIX, "-node");
assert_eq!(node_alias("ciris-agent-bootstrap"), "ciris-agent-bootstrap-node");

It pins edge's own implementation, not the server's. When the server rule changed the mirror forked and this test still passed — it can only ever detect edge editing its own copy, which is the one case that is not the risk. The doc comment above NODE_ALIAS_SUFFIX says "if it ever drifts, that test is where it surfaces"; it did drift, and it did not surface.

Edge is right that it cannot import the constant — CIRISServer rides edge, and a dependency that way inverts the substrate relationship. But that argument rules out importing, not being told.

Impact

Every first-run agent node on server ≥ 0.5.195. Provisioning reports success; edge then fail-closes on a key that does not exist; the node cannot bring up its transport identity at all. There is no caller-side workaround — there is no alias A where substitute(A, agent→node) equals A + "-node", so no choice of host_keystore_alias reconciles the two rules.

Proposed fix

Let the caller name the key. provision_node_identity already returns the minted key_id; the value crosses the boundary on the way out and has nowhere to go on the way back in.

Add an optional node keystore alias to init_edge_runtime (and open_node_identity_halves):

  • when supplied → use it verbatim, no derivation;
  • when absent → fall back to today's node_alias(host_keystore_alias), so existing deployments are unaffected.

That makes the name travel as a parameter and leaves exactly one component naming the key: the one that mints it. Retiring the mirrored node_alias once callers pass the name would remove the whole class.

If the constant should instead live in shared substrate (ciris-verify-core::fedcode is the natural home — it already owns derive_key_id, the function that makes the alias identity-bearing), that works too and is a strictly bigger change. Either way, two implementations of one rule is the thing to remove.

Note on #541

#541 is closed, and it is the issue that introduced this mirror. Edge's code cites it as the rationale, so nothing currently tracks the fork it left behind — hence this issue rather than a comment there.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions