Hosted onifebitcoin.orgvia theHypermedia Protocol

Overview

This document specifies the Daemon gRPC service used to administer a local Seed Daemon instance: key generation/registration, node info, maintenance operations (sync/reindex), raw blob ingestion, device linking, and generic signing.

The API is intentionally low-level and local‑first: it operates the node, key store, and background services. It does not manage application‑level content beyond accepting raw blobs for storage.

Goals

  • Provide a minimal control plane to initialize a node (mnemonic → key → account binding).

  • Offer maintenance controls (force sync/reindex) and diagnostics (GetInfo).

  • Enable device linking (out‑of‑band session exchange) and generic signing.

  • Support multi‑key setups with CRUD operations over named signing keys.

Non‑Goals

  • Defining authorization for app resources (covered by AccessControl/Resources).

  • Managing peer networking directly (see Networking service).

  • Long‑running streaming diagnostics; this API is request/response.

Protocol Definition

syntax = "proto3";

package com.seed.daemon.v1alpha;

import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";

option go_package = "seed/backend/genproto/daemon/v1alpha;daemon";

// Daemon API allows to control and administer the Seed Daemon.
service Daemon {
  // Generates a set of BIP-39-compatible mnemonic words encoding a cryptographic seed.
  // This is a stateless call, and the generated mnemonic is not stored anywhere.
  // Subsequent call to RegisterKey can be used to register a new signing key derived from the mnemonic.
  rpc GenMnemonic(GenMnemonicRequest) returns (GenMnemonicResponse);

  // After generating the seed, this call is used to commit the seed and
  // create an account binding between the device and account.
  rpc RegisterKey(RegisterKeyRequest) returns (NamedKey);

  // Get generic information about the running node.
  rpc GetInfo(GetInfoRequest) returns (Info);

  // Force-trigger periodic background sync of Seed objects.
  rpc ForceSync(ForceSyncRequest) returns (google.protobuf.Empty);

  // Forces the daemon to reindex the entire database.
  rpc ForceReindex(ForceReindexRequest) returns (ForceReindexResponse);

  // Lists all the signing keys registered on this Daemon.
  rpc ListKeys(ListKeysRequest) returns (ListKeysResponse);

  // Updates the existing key.
  rpc UpdateKey(UpdateKeyRequest) returns (NamedKey);

  // Deletes a key from the underlying key store.
  rpc DeleteKey(DeleteKeyRequest) returns (google.protobuf.Empty);

  // Deletes all Seed keys from the underlying key store.
  rpc DeleteAllKeys(DeleteAllKeysRequest) returns (google.protobuf.Empty);

  // Receives raw blobs to be stored.
  // The request may fail if blobs can't be recognized by the daemon.
  rpc StoreBlobs(StoreBlobsRequest) returns (StoreBlobsResponse);

  // Creates a new device link session.
  // The session information has to be transferred to the other device,
  // to establish a direct P2P connection between the devices, and complete the linking process.
  //
  // There can only be one active session at a time, and creating a new one will invalidate the previous one.
  //
  // After the session is redeemed, it becomes invalid.
  rpc CreateDeviceLinkSession(CreateDeviceLinkSessionRequest) returns (DeviceLinkSession);

  // Get the current device link session (if it exists).
  rpc GetDeviceLinkSession(GetDeviceLinkSessionRequest) returns (DeviceLinkSession);

  // Sign arbitrary data with an existing signing key.
  rpc SignData(SignDataRequest) returns (SignDataResponse);
}

// Request to generate mnemonic words.
message GenMnemonicRequest {
  // Optional. Number of mnemonic words to encode the seed.
  // Usually 12 or 24 words.
  // By default 12 words are generated.
  int32 word_count = 1;
}

// Response with the generated mnemonic.
message GenMnemonicResponse {
  // The list of human-friendly words that can be used to backup the seed. These
  // words must be stored in a secret place by the user.
  repeated string mnemonic = 1;
}

// Request to register a new account key derived from the mnemonic.
message RegisterKeyRequest {
  // Required. The list of BIP-39 mnemonic words.
  repeated string mnemonic = 1;

  // Optional. Passphrase for the seed.
  string passphrase = 2;

  // Required. Private name/label for the signing key, to easily identify keys when they are more than one.
  // Name must be unique across all the registered keys.
  string name = 3;
}

// Request to get basic information about the running daemon.
message GetInfoRequest {}

// Request to force the syncing process.
message ForceSyncRequest {}

// Request to force reindexing of the entire database.
message ForceReindexRequest {}

// Response after forcing reindexing.
message ForceReindexResponse {}

// Request to delete all keys.
message DeleteAllKeysRequest {}

// Request to list signing keys.
message ListKeysRequest {}

// Response with the list of registered signing keys.
message ListKeysResponse {
  // List of registered keys.
  repeated NamedKey keys = 1;
}

// Request to change the key name.
message UpdateKeyRequest {
  // Current name of the key.
  string current_name = 1;

  // New name for the key.
  string new_name = 2;
}

// Request to delete an existing key.
message DeleteKeyRequest {
  // Name of the key to delete.
  string name = 1;
}

// Request to store blobs.
message StoreBlobsRequest {
  // Required. List of blobs to be stored.
  // The request is atomic: either all blobs are stored or none of them.
  repeated Blob blobs = 1;
}

// Response after storing blobs.
message StoreBlobsResponse {
  // List of CIDs for the stored blobs.
  // The order is the same as in the request.
  repeated string cids = 1;
}

// Request to create a new device link session.
message CreateDeviceLinkSessionRequest {
  // Required. Name of the signing key for which to create the delegation.
  string signing_key_name = 1;

  // Optional. Label that will be used for the newly created capability.
  // The label is publicly visible.
  string label = 2;
}

// Request to get the device link session.
message GetDeviceLinkSessionRequest {}

// Request to sign data.
message SignDataRequest {
  // Required. Name of the signing key to use for signing.
  string signing_key_name = 1;

  // Required. Data to be signed.
  bytes data = 2;
}

// Response for signing data.
message SignDataResponse {
  // Signature over the data.
  bytes signature = 1;
}

// Information about the device link session.
message DeviceLinkSession {
  // Dial information for the node.
  AddrInfo addr_info = 1;

  // Secret token for the linking session.
  string secret_token = 2;

  // Account ID that wants to link the new device.
  string account_id = 3;

  // Label for the future capability as defined by the user.
  string label = 4;

  // Expiration time of the session.
  google.protobuf.Timestamp expire_time = 5;

  // Optional. Time when the session was redeemed,
  // i.e. when the device link exchange was completed successfully.
  google.protobuf.Timestamp redeem_time = 6;
}

// Address information about a single peer.
message AddrInfo {
  string peer_id = 1;

  repeated string addrs = 2;
}

// Raw blob to be stored.
message Blob {
  // Optional. The client can provide a CID for the blob (the server will verify it).
  // If not provided, the data is assumed to be DAG-CBOR encoded, and the server will generate a CID
  // using its default hash function.
  string cid = 1;

  // Required. Raw data of the blob.
  bytes data = 2;
}

// Info is a generic information about the running node.
message Info {
  // Current state of the daemon.
  State state = 1;

  // Libp2p Peer ID of this node.
  string peer_id = 2;

  // Start time of the node.
  google.protobuf.Timestamp start_time = 3;

  // The libp2p protocol ID that the daemon is using.
  string protocol_id = 4;
}

// State describes various states of the daemon.
enum State {
  // Daemon in starting up and it not ready to use yet.
  STARTING = 0;

  // Daemon is running a data migration, which may take a while.
  // Callers should poll and wait until the daemon becomes ACTIVE.
  MIGRATING = 1;

  // Daemon is active and ready to use.
  ACTIVE = 3;
}

// Signing key with an internal name.
message NamedKey {
  // Public key in Multikey format.
  // https://www.w3.org/TR/vc-data-integrity/#multikey.
  string public_key = 1;

  // Private name for the key. Useful to identify the keys when there're more than one.
  string name = 2;

  // Account ID representation of this key.
  string account_id = 3;
}

Service

Daemon exposes administrative RPCs:

  • GenMnemonic(GenMnemonicRequest) → GenMnemonicResponse — Generate BIP‑39 mnemonic words for seed backup (stateless, not stored by the daemon).

  • RegisterKey(RegisterKeyRequest) → NamedKey — Import/derive a key from mnemonic + optional passphrase and bind an Account ID, storing under a unique local name.

  • GetInfo(GetInfoRequest) → Info — Snapshot of daemon state, peer ID, start time, and protocol id.

  • ForceSync(ForceSyncRequest) → Empty — Kick the background sync loop.

  • ForceReindex(ForceReindexRequest) → ForceReindexResponse — Trigger a full database reindex.

  • ListKeys(ListKeysRequest) → ListKeysResponse — List all registered signing keys.

  • UpdateKey(UpdateKeyRequest) → NamedKey — Rename an existing key (local alias only).

  • DeleteKey(DeleteKeyRequest) → Empty — Delete a specific key from the keystore.

  • DeleteAllKeys(DeleteAllKeysRequest) → Empty — Purge all Seed keys from the keystore.

  • StoreBlobs(StoreBlobsRequest) → StoreBlobsResponse — Atomically ingest raw blobs, returning CIDs in request order.

  • CreateDeviceLinkSession(CreateDeviceLinkSessionRequest) → DeviceLinkSession — Create a short‑lived session for secure device linking.

  • GetDeviceLinkSession(GetDeviceLinkSessionRequest) → DeviceLinkSession — Fetch the current active session, if any.

  • SignData(SignDataRequest) → SignDataResponse — Sign arbitrary bytes with a named key.

Messages

Mnemonic & Keys

GenMnemonicRequest

  • word_count (int32, optional) — Typically 12 or 24; server may cap to supported sizes.

GenMnemonicResponse

  • mnemonic (string[]) — BIP‑39 word list; must be stored securely by the user.

RegisterKeyRequest

  • mnemonic (string[], required) — The BIP‑39 words.

  • passphrase (string, optional) — Optional BIP‑39 passphrase.

  • name (string, required) — Unique local alias for this key.

NamedKey

  • public_key (string) — Multikey format (W3C Data Integrity spec).

  • name (string) — Local alias.

  • account_id (string) — Account representation derived from the key.

Node Info & Maintenance

GetInfoRequest — Empty.

Info

  • state (State) — STARTING | MIGRATING | ACTIVE.

  • peer_id (string) — libp2p peer ID of this node.

  • start_time (Timestamp) — Node start timestamp.

  • protocol_id (string) — libp2p protocol id.

ForceSyncRequest, ForceReindexRequest, ForceReindexResponse — Maintenance controls.

Key Management

ListKeysRequest, ListKeysResponse.keys: NamedKey[] — Enumerate keys.

UpdateKeyRequest

  • current_name (string) — Existing alias.

  • new_name (string) — New unique alias.

DeleteKeyRequest

  • name (string) — Alias to remove.

DeleteAllKeysRequest — Purge keystore.

Blobs

StoreBlobsRequest.blobs: Blob[] — Atomic batch of blobs.

Blob

  • cid (string, optional) — Client‑provided CID (server validates). When absent, server assumes DAG‑CBOR and computes CID with default hash.

  • data (bytes, required) — Raw payload.

StoreBlobsResponse.cids: string[] — CIDs in same order as request.

Device Linking

CreateDeviceLinkSessionRequest

  • signing_key_name (string, required) — The key to delegate from.

  • label (string, optional) — Public label for the capability created as part of linking.

DeviceLinkSession

  • addr_info (AddrInfo) — Peer id + multiaddrs to dial.

  • secret_token (string) — One‑time linking secret to authenticate the session.

  • account_id (string) — Account initiating the link.

  • label (string) — Future capability label.

  • expire_time (Timestamp) — Session expiration.

  • redeem_time (Timestamp, optional) — When the session was successfully redeemed.

GetDeviceLinkSessionRequest — Empty.

AddrInfo

  • peer_id (string) — libp2p peer ID.

  • addrs (string[]) — Dialable multiaddrs.

Signing

SignDataRequest

  • signing_key_name (string, required) — Alias of key to use.

  • data (bytes, required) — Bytes to sign.

SignDataResponse.signature (bytes) — Signature over data.

State Enum

State

  • STARTING — Node is booting.

  • MIGRATING — Node is migrating data; wait until ACTIVE.

  • ACTIVE — Node is ready.

Field Semantics & Validation

  • Mnemonic sizes — Server SHOULD accept only supported BIP‑39 sizes (e.g., 12/24); reject others with INVALID_ARGUMENT.

  • Key names — Must be unique, non‑empty, and normalized (case policy documented by server). Renames must be atomic.

  • DeleteAllKeys — Irreversible purge; consider confirmation at higher layers.

  • Blobs — When cid is provided, server MUST verify integrity; otherwise, assume DAG‑CBOR and compute canonical CID. Batch is atomic.

  • Device link session — Only one active session at a time; creating a new one invalidates the previous. Sessions expire at expire_time or upon successful redemption.

  • SignData — Server signs exactly the provided bytes; callers must apply their own domain separation and hashing policy.

Errors

Representative gRPC status codes:

  • INVALID_ARGUMENT — Unsupported word_count, malformed mnemonic, duplicate/new key name invalid, bad CID, empty blob list.

  • ALREADY_EXISTSRegisterKey or UpdateKey name collision.

  • NOT_FOUND — Missing key alias on UpdateKey/DeleteKey/SignData.

  • FAILED_PRECONDITION — Another active device link session exists and cannot be replaced per policy.

  • RESOURCE_EXHAUSTED — Keystore/quota limits; blob size exceeds limits.

  • UNAVAILABLE — Maintenance subsystems down (sync/reindex).

  • DEADLINE_EXCEEDED — Long‑running ForceReindex (if bounded by deadline).

  • PERMISSION_DENIED — Caller lacks admin privileges for the daemon.

  • INTERNAL — Unexpected server error.

Security & Permissions

  • Mnemonic handlingGenMnemonic is stateless; never log or persist mnemonics. Encourage users to store mnemonics offline.

  • Keystore — Keys must be stored encrypted at rest; renames and deletes should scrub metadata.

  • Device linkingsecret_token is sensitive; transmit via secure OOB channel. Sessions must be short‑lived and single‑use.

  • Signing — Apply domain separation; record key usage for audit (who signed what/when) without leaking payloads.

  • Blobs — Validate and cap sizes; verify CIDs to prevent hash‑confusion.

Versioning Strategy

  • Additive message fields are backwards compatible; breaking changes require new package (e.g., v1beta).

  • Introduce new maintenance RPCs feature‑flagged when needed.

Examples

grpcurl — Generate Mnemonic (24 words)

grpcurl api.seed.hyper.media:443 \
  com.seed.daemon.v1alpha.Daemon.GenMnemonic \
  '{"word_count":24}'

grpcurl — Register Key

grpcurl -H "Authorization: Bearer $TOKEN" \
  api.seed.hyper.media:443 com.seed.daemon.v1alpha.Daemon.RegisterKey \
  '{"mnemonic":["mystery","apple",...],"passphrase":"","name":"primary"}'

grpcurl — Get Info

grpcurl api.seed.hyper.media:443 com.seed.daemon.v1alpha.Daemon.GetInfo '{}'

grpcurl — Force Sync

grpcurl -H "Authorization: Bearer $TOKEN" \
  api.seed.hyper.media:443 com.seed.daemon.v1alpha.Daemon.ForceSync '{}'

grpcurl — Store Blobs (two items)

grpcurl -d '{"blobs":[{"data":"base64..."},{"cid":"bafy...","data":"base64..."}]}' \
  api.seed.hyper.media:443 com.seed.daemon.v1alpha.Daemon.StoreBlobs

grpcurl — Device Link Session

grpcurl -H "Authorization: Bearer $TOKEN" \
  api.seed.hyper.media:443 com.seed.daemon.v1alpha.Daemon.CreateDeviceLinkSession \
  '{"signing_key_name":"primary","label":"MacBook Pro"}'

grpcurl — Sign Data

echo -n "hello" | base64 | \
  xargs -I{} grpcurl -d '{"signing_key_name":"primary","data":"{}"}' \
  api.seed.hyper.media:443 com.seed.daemon.v1alpha.Daemon.SignData

Client Guidelines

  • Treat key names as local aliases; store the returned account_id for cross‑device identity.

  • Show strong warnings for mnemonic handling; recommend 24 words for higher entropy when appropriate.

  • For StoreBlobs, preserve response order to map CIDs back to inputs.

  • For device linking, display countdown to expire_time and clear session state after redemption.

Server Guidelines

  • Enforce rate limits on GenMnemonic, RegisterKey, and SignData to mitigate abuse.

  • Implement atomic rename for keys and ensure durable keystore writes.

  • Ensure DeleteAllKeys scrubs all secrets and metadata; require elevated authorization.

  • Limit blob sizes, validate CID formats, and prefer streaming ingestion in future versions.

  • Allow only one active device link session and invalidate previous sessions upon creation.

Future Work

  • Streaming logs/metrics for maintenance operations.

  • Export/import keystore (encrypted backup/restore).

  • Hardware key support (WebAuthn/FIDO, Ledger) for SignData.

  • Add RevokeDeviceLinkSession and session introspection.

  • Streaming blob ingest and CAR import.

Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime