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