README
¶
sdk-go
Go SDK for the Genesis Mesh Network Authority HTTP API.
Go ≥ 1.22 required. Zero runtime dependencies (stdlib only + github.com/google/uuid).
Scope
This SDK (1.6) covers the Network Authority's trust API: the sub-clients
below (agreements, boundary decisions, trust evidence, attestations,
selective disclosure, consensus and data usage), offline verification of
their signed records, and signed admin calls to any other route. It has no
governed lifecycle API: no evidence store client or evidence outbox, none
of the 1.3.0 changes outside the controlled path (observations, break-glass
records, the record outbox), and none of the 1.4.0 remediation and review
APIs (remediation records, the due queue, reviews). Those live in the
TypeScript SDK and the
Rust SDK. The embedded field
registry lists their records, but this SDK does not verify evidence exports.
A boundary policy with the 1.4.0 sections (remediation, review_required,
emergency) verifies here like any other.
From 1.5 it reads the NA's key history (see
NA key history), so decisions and agreements signed before
and after a key succession keep verifying. Audit packs, archives and their
verification are not part of this SDK: they live in the core
(genesis-mesh audit pack, genesis-mesh audit verify) and in the Rust
SDK's independent verifier genesis-mesh-verify.
From 1.6 it checks a pre-issued grant's NA signature (see Pre-issued grants). Requesting grants, holding time tokens, acting within a grant offline, recording its uses and falling back to break-glass live in the TypeScript SDK and the Rust SDK, as does verifying grants, their uses and revocations in an evidence export.
Install
go get github.com/GenesisMeshLabs/sdk-go@latest
Quick start
package main
import (
"context"
"os"
"github.com/GenesisMeshLabs/sdk-go/genesismesh"
)
func main() {
client, err := genesismesh.NewClient(genesismesh.ClientOptions{
BaseURL: "http://127.0.0.1:9443",
SigningKey: os.Getenv("OPERATOR_KEY"), // base64-encoded 32-byte Ed25519 seed
KeyID: "operator-local", // must match the NA's registered key
})
if err != nil {
panic(err)
}
decision, err := client.Boundary.Decide(context.Background(), map[string]interface{}{
"requesting_agent_id": "agent-a",
"capability": "transactions.read",
})
if err != nil {
panic(err)
}
_ = decision
}
Local Network Authority
To develop against a governed Network Authority on your machine (policies
required, a privileged key for setup and a standard key for your controller),
see Develop Against a Local Network Authority.
It needs genesis-mesh 1.1.0 or later from PyPI.
Sub-clients
Agreement
// Create a capability offer (admin — requires signing key)
offer, err := client.Agreement.Offer(ctx, genesismesh.CapabilityOffer{
OfferorSovereignID: "ALPHA-NA",
ResponderSovereignID: "BETA-NA",
Capabilities: []string{"read:data", "write:log"},
Roles: []string{"role:client"},
ValidityHours: 8760,
})
// Counter an offer
counter, err := client.Agreement.Counter(ctx, map[string]interface{}{
"offer": offer,
"capabilities": []string{"read:data"},
"validity_hours": 4380,
})
// Accept an offer (requires the NA to hold a recognition treaty for the responder)
agreement, err := client.Agreement.Accept(ctx, map[string]interface{}{"offer": offer})
// Verify agreement signatures (no auth required)
result, err := client.Agreement.Verify(ctx, map[string]interface{}{"agreement": agreement})
Boundary
// Issue a boundary decision (admin)
decision, err := client.Boundary.Decide(ctx, map[string]interface{}{
"agreement": agreement,
"requested_capability": "read:data",
})
// Verify a boundary decision (no auth required)
v, err := client.Boundary.Verify(ctx, map[string]interface{}{"decision": decision})
Evidence
// Build signed trust evidence (admin)
// verdict must be one of: "allow" | "block" | "escalate" | "warn"
evidence, err := client.Evidence.Build(ctx, genesismesh.TrustDecision{
SourceSovereignID: "ALPHA",
TargetSovereignID: "BETA",
Verdict: "allow",
Reason: "long-standing member",
})
Attestation
// Issue a membership attestation (admin)
// roles must use a recognized prefix: role:anchor | role:bridge | role:client | role:operator | role:service:<name>
att, err := client.Attestation.Issue(ctx, genesismesh.MembershipAttestation{
SubjectID: "node-xyz",
Roles: []string{"role:client"},
ValidityHours: 8760,
})
// Revoke an attestation (admin)
err = client.Attestation.Revoke(ctx, att.AttestationID, map[string]string{"reason": "key compromised"})
// Set recognition policy (admin)
err = client.Attestation.SavePolicy(ctx, genesismesh.RecognitionPolicy{
LocalSovereignID: "MY-NA",
RecognizedIssuers: []genesismesh.RecognizedIssuer{},
})
Disclosure
// Commit to a set of capabilities (admin)
commitment, err := client.Disclosure.Commit(ctx, genesismesh.CapabilityCommitment{
Capabilities: []string{"read:data", "write:log"},
})
// Generate a Merkle membership proof (no auth required)
proof, err := client.Disclosure.Prove(ctx, genesismesh.CapabilityMembershipProof{
Capability: "read:data",
ProverSovereignID: "BETA",
})
// Issue a one-time nullifier (admin)
nullifier, err := client.Disclosure.Nullifier(ctx, map[string]interface{}{"proof": proof})
// Verify the proof (no auth required)
dv, err := client.Disclosure.Verify(ctx, map[string]interface{}{"proof": proof})
Consensus
Validators vote on a justification proof: the justification_proof returned
with a decision by POST /admin/boundary/evaluate (see Raw admin calls). Pass
votes and proofs back unchanged; every field is covered by a signature.
// Cast a validator vote, signed by the NA as validator (admin)
vote, err := client.Consensus.Vote(ctx, map[string]interface{}{
"justification_proof": justificationProof,
"vote": true,
"reason": "evidence satisfactory",
})
// Assemble a K-of-N consensus proof (admin)
cp, err := client.Consensus.Proof(ctx, map[string]interface{}{
"justification_proof": justificationProof,
"votes": []genesismesh.ConsensusVote{*vote},
"required_threshold": 1,
"validator_sovereign_ids": []string{vote.ValidatorSovereignID},
})
// Verify against the validators' keys (no auth required)
cv, err := client.Consensus.Verify(ctx, map[string]interface{}{
"proof": cp,
"validator_public_keys": map[string]string{vote.ValidatorSovereignID: validatorPublicKey},
})
// cv.Valid, cv.Reason ("valid", "threshold_not_met", "invalid_vote_signature", ...)
DataUsage
// Create a data license policy (admin)
pol, err := client.DataUsage.CreatePolicy(ctx, genesismesh.DataLicensePolicy{
LicenseeSovereignID: "BETA",
AllowedSourceIDs: []string{"src-a"},
AllowedAccessTypes: []string{"read", "aggregate"},
ValidFrom: "2026-01-01T00:00:00Z",
ValidUntil: "2026-12-31T00:00:00Z",
})
// Create a data access intent (admin)
// source_type: "personal" | "proprietary" | "public" | "synthetic"
intent, err := client.DataUsage.CreateIntent(ctx, genesismesh.DataAccessIntent{
Sources: []genesismesh.DataSourceDescriptor{{
SourceID: "src-a",
SourceType: "public",
OwnerSovereignID: "MY-NA",
}},
AccessTypes: []string{"read"},
})
// Get the active policy (no auth required)
activePol, err := client.DataUsage.GetPolicy(ctx)
// Verify intent against policy (no auth required)
dv, err := client.DataUsage.Verify(ctx, map[string]interface{}{"intent": intent, "policy": pol})
Offline verification
From 0.61.0 the SDK verifies Network Authority records locally, using canonical JSON and Ed25519 only, without calling the NA. The reason codes are the Python reference's. Pass the JSON as received.
a, err := genesismesh.VerifyAgreement(agreementJSON, []string{offererKey}, []string{responderKey}, "")
// a.Accepted, a.Reason: "accepted", "invalid_offerer_signature", "graph_digest_mismatch", ...
d, err := genesismesh.VerifyBoundaryDecision(decisionJSON, genesismesh.DecisionVerifyOptions{
OperatorPublicKeys: []string{naPublicKey},
ExpectedPolicies: [][]byte{policyJSON}, // the decision must bind exactly these versions
ExpectedAttestation: attestationJSON, // optional
})
// d.Accepted (verified), d.Authorized (ALLOW or DENY), d.Reason
ok, err := genesismesh.VerifyDataLicensePolicySignature(policyJSON, []string{licensorKey})
v, err := genesismesh.VerifyDataAccessIntent(intentJSON, policyJSON, []string{agentKey}, time.Now())
// v.Valid, v.ViolationReason, v.Violations
An accepted DENY has Accepted true and Authorized false: the decision
verified, and it denies. Revocation is not checked offline.
NA key history
From 1.5.0 a Network Authority can move to a new signing key (a key
succession, signed by its offline root key and by the outgoing key). Pass the
NA's key history and a record must carry the signature of the key valid at
the record's own time: a decision's decision_made_at, an agreement's
agreed_terms.valid_from (NARecordTimeFields lists the time of each
NA-signed record). A succeeded key is still accepted for 300 seconds after
the next key takes effect (SuccessionHandover). A decision may not stay
valid more than MaxDecisionLifetime (300 seconds) past the end of the key
that signed it, and an agreement vouches only while its key is still
accepted. Without a history nothing changes: a list of keys is valid at every
time.
// The body of GET /na/key-history, checked from the root key you hold out of band.
h, err := genesismesh.VerifyKeyHistoryResponse(body, rootPublicKey)
// h.Verified; otherwise h.Reason ("genesis_root_mismatch", "succession_chain_break", ...)
// and h.KeySequence, the refused record's position.
d, err := genesismesh.VerifyBoundaryDecision(decisionJSON, genesismesh.DecisionVerifyOptions{
KeyHistory: h.History, // instead of OperatorPublicKeys
AtRecordTime: true, // optional: an archived decision, judged as of decision_made_at
})
// d.Reason "na_key_not_valid_at_time": signed by a key of the history, but not the one valid then;
// "decision_outlives_key": valid longer than its key may vouch for.
a, err := genesismesh.VerifyAgreementWithKeyHistory(agreementJSON, h.History,
genesismesh.NAKeysValidAlways([]string{responderKey}), "", time.Now())
VerifyKeyHistory(genesisJSON, successionsJSON, rootPublicKey) takes the
genesis block and the succession records directly. The refusal reasons and
their order are the reference's: genesis_invalid, genesis_root_mismatch,
unknown_field, non_canonical_form, genesis_invalid_signature, then for
each succession payload_invalid, unknown_field or invalid_signature,
non_canonical_form and the succession_* reasons. A succession carries
three signatures: the root key's, the new key's (proof that it is held) and
the outgoing key's. The SDK runs the key history vectors of the shared
audit suite (genesismesh/testdata/conformance/audit.json).
CanonicalJSON, PolicyDigest, AttestationDigest and PolicySetDigest
reproduce the reference forms. The verifiers pass the shared interop
conformance vectors (genesismesh/testdata/conformance/interop.json), and the
core's cross-language scenario runs them against a live NA.
Pre-issued grants
From 1.6.0 the NA can issue a CapabilityGrant before the bearer acts, so
the bearer can act within it while the NA is unreachable. Grants are off
unless the NA runs with EVIDENCE_GRANTS=on. VerifyCapabilityGrant checks
that a grant is the NA's: signed by the key of its key history valid at the
grant's issued_at, with every signed field known and in canonical form.
g, err := genesismesh.VerifyCapabilityGrant(grantJSON, h.History) // or NAKeysValidAlways([]string{naKey})
// g.Valid; otherwise g.Reason: "missing_signature", "invalid_signature", "unknown_field",
// "non_canonical_form" or "na_key_not_valid_at_time".
It does not check the grant's validity, budget, parameter envelope or
revocation. The NA's revocation feed lists revoked grants in
revoked_grant_ids while grants are on (left out while they are off); the
embedded field registry knows both forms. The SDK runs the signed-form and
signature vectors of the shared grants suite
(genesismesh/testdata/conformance/grants.json).
Raw admin calls
For NA routes not yet covered by a sub-client (e.g. /admin/recognition-treaties),
use BuildAdminHeaders directly:
import (
"bytes"
"encoding/json"
"net/http"
"github.com/GenesisMeshLabs/sdk-go/genesismesh"
)
priv, _, _ := genesismesh.LoadPrivateKey(os.Getenv("OPERATOR_KEY"))
body := map[string]interface{}{
"subject_sovereign_id": "BETA-NA",
"subject_public_keys": []string{"<base64-ed25519-pubkey>"},
"scope": map[string]interface{}{"allowed_roles": []string{"role:client"}},
"validity_hours": 24,
}
headers, err := genesismesh.BuildAdminHeaders(genesismesh.AdminRequest{
Method: "POST", Path: "/admin/recognition-treaties",
Audience: "<NA public key>", // network_authority.public_key from the NA's /sovereign.json
Body: body,
}, "operator-local", priv)
b, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", baseURL+"/admin/recognition-treaties", bytes.NewReader(b))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Admin-Key-Id", headers.KeyID)
req.Header.Set("X-Admin-Signature", headers.Signature)
req.Header.Set("X-Admin-Timestamp", headers.Timestamp)
req.Header.Set("X-Admin-Nonce", headers.Nonce)
Error handling
import "errors"
_, err := client.Agreement.Offer(ctx, genesismesh.CapabilityOffer{})
switch {
case err == nil:
// success
default:
var unauthorized *genesismesh.UnauthorizedError
var rateLimit *genesismesh.RateLimitError
var validation *genesismesh.ValidationError
var network *genesismesh.NetworkError
switch {
case errors.As(err, &unauthorized):
// bad signing key or stale timestamp
case errors.As(err, &rateLimit):
// back off and retry
case errors.As(err, &validation):
// inspect err.Code and err.Message
case errors.As(err, &network):
// connection refused or timeout — inspect err.Cause
}
}
Admin authentication
Admin routes are authenticated with four HTTP headers built from an Ed25519 operator key:
| Header | Description |
|---|---|
X-Admin-Key-Id |
Key identifier registered with the NA |
X-Admin-Signature |
Ed25519 over the canonical admin payload: {v: 2, method, path, query, audience, body, key_id, timestamp, nonce} |
X-Admin-Timestamp |
ISO 8601 UTC timestamp (must be within NA's nonce window) |
X-Admin-Nonce |
UUID v4 replay-protection token (single use) |
Signature version 2 (1.0.2) binds each signature to the HTTP method, the request
path, the query parameters and the target NA's public key (Audience). The
client reads it (network_authority.public_key) once from the NA's public
/sovereign.json, or
uses ClientOptions.Audience. AdminSigningPayload returns the signed bytes.
The SDK handles all of this automatically when SigningKey is provided.
Build and test
go build ./...
go test -race ./...
go vet ./...
License
MIT
Directories
¶
| Path | Synopsis |
|---|---|
|
Package genesismesh provides a typed client for the Genesis Mesh Network Authority HTTP API.
|
Package genesismesh provides a typed client for the Genesis Mesh Network Authority HTTP API. |