Help us shape the future of Go! Take the 2026 Go Developer Survey

sdk-go

module
v1.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2026 License: MIT

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL