Languages: Python · TypeScript

10-minute cold start

From an empty environment to a verified, signed GRIP (v0.1, stable) record in five steps. Setup takes about ten minutes, most of it the dependency install in step 1.

Step What happens
1. Install pip install grip-sdk (Python) or npm install @grip/sdk (TypeScript)
2. Start a sandbox MockSandbox() — in-process, no server, no network. A hosted sandbox zone with the same surface over HTTP/WS is coming soon.
3. Register register(...) with a generated Ed25519 keypair — returns a signed identity.cert
4. Request an envelope request_envelope(...) / requestEnvelope(...) — returns a signed envelope.grant, your first signed GRIP record
5. Verify check the record's signature and transparency-log inclusion (SPEC.md §6.2)

The quickstart below runs the full register → envelope → telemetry → signal flow with zero network calls. When you point at a live sandbox zone instead, the same steps run over HTTP — the wire-level version of steps 3-5:

# 1. Register (P1) — issues an identity.cert
curl -s -X POST http://127.0.0.1:8765/v1/register -d '{
  "node_label": "coldstart-demo",
  "public_key": "<base64url ed25519 pubkey>",
  "asset_class": "storage",
  "capability": {"import_max_w": 500000, "export_max_w": 500000, "ramp_max_w_s": 100000, "energy_capacity_wh": 2000000, "control_latency_ms": 500},
  "firmness": "flexible"
}'

# 2. Request an envelope (P2) — requires a `GRIP-Auth` cert-key
#    possession-proof header (SPEC.md §7.1); node_id comes from the
#    verified cert, not a query param. Raw curl can't sign the header
#    inline — both SDKs do this for you:
#      Python:     GripClient(base_url).get_envelope(keypair)
#      TypeScript: client.getEnvelope(keypair)
# The header shape, if you're hand-rolling a client:
#   GRIP-Auth: kid=<cert kid> ts=<rfc3339 utc ms> nonce=<16 hex> sig=<base64url Ed25519 sig>
curl -s http://127.0.0.1:8765/v1/envelope \
  -H 'GRIP-Auth: kid=<cert kid> ts=<rfc3339 utc ms> nonce=<16 hex> sig=<base64url sig>'

# 3. Confirm transparency-log inclusion (SPEC.md §6.2)
curl -s "http://127.0.0.1:8765/v1/log/entries?start=0"
curl -s "http://127.0.0.1:8765/v1/log/proof?leaf_index=0"

grip-sdk (Python)

Python client SDK for GRIP protocol v0.1 (stable).

Install

pip install grip-sdk

Requires Python >= 3.10. Runtime dependencies: cryptography (Ed25519), httpx (HTTP client), websockets (signal subscription).

Quickstart

Registers a storage asset, requests an operating envelope, signs and submits a telemetry report, and watches a few congestion-signal ticks — all against the in-process MockSandbox, so it runs with no server and no network access:

from grip_sdk.keys import KeyPair
from grip_sdk.mock_sandbox import MockSandbox  # same method surface as GripClient
from grip_sdk.types import Capability, EnvelopeRequest

sandbox = MockSandbox()
keypair = KeyPair.generate()  # private key never leaves this process (SPEC.md sec 1.5)

capability = Capability(
    import_max_w=500_000, export_max_w=500_000, ramp_max_w_s=100_000,
    energy_capacity_wh=2_000_000, control_latency_ms=500,
)
cert, chain = sandbox.register("bldg7", "storage", capability, "flexible", keypair)
node_id = cert.payload["node_id"]

grant = sandbox.request_envelope(
    EnvelopeRequest(node_id=node_id, want_import_w=450_000, want_export_w=0, want_duration_s=900),
    keypair,
)
print(grant.payload["import_max_w"], grant.payload["valid_until"])

When the hosted sandbox goes live, swap MockSandbox() for GripClient("https://sandbox.protocol.standardpower.co") — every call above is unchanged; both expose the identical method surface.

grip verify (SPEC.md sec 6.3)

Verifies a GripRecord against a live zone's PUBLIC surfaces (federation chain, log checkpoint, inclusion proof) with no auth required — or, with --vectors, sanity-checks this build's own canonicalizer against the Appendix A vectors instead:

grip verify --vectors
grip verify envelope-grant.json --registrar-url https://sandbox.protocol.standardpower.co

Output is one of VALID / SIGNED-NOT-LOGGED / INVALID (SPEC.md sec 6.3) plus the resolved zone, signer kid, and any checkpoint cosigners. Pin root keys out of band with --roots roots.json for a real deployment; --registrar-url-fetched roots are a sandbox/demo convenience only (see grip_sdk.verify.RegistrarReader.federation_roots's docstring) and MUST NOT be trusted for anything but the sandbox.

For asset-signed records (e.g. telemetry.report), pass the signing asset's identity.cert with --cert. grip_sdk.verify.verify_record is the same logic, callable directly for programmatic verification.

Conformance

The SDK's test suite reproduces the spec's Appendix A canonical-JSON vectors (V1-V4) byte-for-byte — the cross-language conformance contract shared with the TypeScript SDK — plus Ed25519 sign/verify, payload schema round-trips, and the full mock-sandbox flow.

Module reference

Module Contents
grip_sdk.canonical canonicalize(value) -> bytes, is_signable(value) -> bool, NotSignableError — GRIP-CJ (RFC 8785 restricted profile), SPEC.md sec 2
grip_sdk.identifiers record_hash(record), sig_preimage(record), kid_for_public_key(raw_pub), instrument_id(listing), SPEC.md sec 2.3 / 4.2
grip_sdk.keys KeyPair (generate / from_raw_private_key / sign), public_key_from_b64url, verify — Ed25519, SPEC.md sec 1.5
grip_sdk.records GripRecord, new_record, sign, verify_record_signature — the signed wrapper, SPEC.md sec 1.5
grip_sdk.types Money, Firmness, AssetClass, and one dataclass per record kind (IdentityCert, EnvelopeGrant, SignalTick, TelemetryReport, ExchangeListing, …) — SPEC.md sec 3-6
grip_sdk.errors GripError, GripProtocolError and per-urn:grip:error:* subclasses, from_problem_json — SPEC.md sec 8
grip_sdk.client GripClient — real HTTP/WS client for a zone's DEV CONSOLE surface (register, envelope, telemetry, signal subscription)
grip_sdk.mock_sandbox MockSandbox — in-process stand-in for a live sandbox zone, same method surface as GripClient
grip_sdk.brand BRAND — the single brand config point (SPEC.md sec 9); never hardcode a display name/domain elsewhere in this SDK
grip_sdk.merkle root_from_audit_path — RFC 6962 Merkle math, independent of any server-side copy, SPEC.md sec 6.2
grip_sdk.verify verify_record, verify_delegation_chain, RegistrarReader, VerificationReport — the sec 6.3 algorithm
grip_sdk.cli main() — the grip verify console script ([project.scripts])

Every quantity in grip_sdk.types is an integer in the units of SPEC.md sec 1.3 (_w, _w_s, _wh, _bp, _ms/_s); every money value is a Money(amount, currency, per=...) — integer minor units + ISO 4217, never a float.

Errors are UX

SDK errors carry the urn:grip:error:* type token from SPEC.md sec 8 and a hint telling you what to do next:

from grip_sdk.errors import GripProtocolError

try:
    client.get_envelope(keypair)
except GripProtocolError as e:
    print(e.type_urn)  # "urn:grip:error:envelope-expired"
    print(e.hint)       # what to do about it