clearing

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Jun 29, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

README

clearing-sdk-go

Go Reference CI

Official Go SDK for the Clearing economic-principal (EPID) service.

Clearing assigns every economic principal — humans, services, agents, organizations, and providers — a stable EPID and a canonical kind, and exposes signed, auditable operations for resolution, sourced writes, and identity unification. This module is self-contained: the resilient resolver, the RSA request-signing primitive, and the canonical-kind taxonomy are all vendored under internal/, so it has no dependency on any private repository.

Install

go get github.com/JetV/clearing-sdk-go
import clearing "github.com/JetV/clearing-sdk-go"

Capability tiers

The SDK is layered so each caller only takes the capability (and trust) it needs. The tier is the permission boundary — constructing L2/L3 requires a source identity and private key, so a read-only consumer cannot obtain write/unify handles from the type system alone.

Tier Constructor Capability
L1 — read NewReadOnly(baseURL, opt) Resolve / GetByEPID / Kinds (cache + circuit breaker)
L2 — source NewSource(baseURL, sourceID, rsaKey, opt) source-signed Ensure / Link / Affiliate
L3 — unify NewUnify(baseURL, sourceID, rsaKey, opt) ProveKey / SubmitVerifiedAttr / Bind / LinkRealm

Quick start (L1, read-only)

package main

import (
	"context"
	"fmt"
	"log"

	clearing "github.com/JetV/clearing-sdk-go"
)

func main() {
	c := clearing.NewReadOnly("https://clearing.internal", clearing.Options{})
	r, err := c.L1.Resolve(context.Background(), clearing.Identity{
		AuthInstanceID: "auth.local",
		Kind:           "user",
		Key:            "alice@example.com",
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(r.EPID, r.CanonicalKind, r.Status)
}

Resilience and degradation

The L1 read tier ships an in-process TTL cache, single-flight request deduplication, and a circuit breaker. It distinguishes authoritative absence (ErrNotRegistered, safe to treat as "not found") from transient unreachability (ErrUnavailable) and never silently fabricates a fallback — the caller decides fail-open vs fail-closed:

switch {
case errors.Is(err, clearing.ErrNotRegistered):
	// principal is authoritatively absent
case errors.Is(err, clearing.ErrUnavailable):
	// clearing is unreachable / circuit open — you decide
}

Kinds is server-first with a compiled-in fallback; the result is flagged Degraded() when it came from the fallback.

Canonical JSON and signing

For source-signed writes (L2) and unify (L3) operations the SDK produces deterministic canonical JSON (sorted keys, no insignificant whitespace, Go encoding/json HTML escaping) and signs it with RS256 (RSA-PKCS1v15 + SHA-256), base64-std. The three signing headers (X-Clearing-Source, X-Clearing-Signature, X-Clearing-Timestamp), the Ed25519 challenge flow, and the rule that an attribute assertion's verifier_sig is signed over the body excluding itself are all handled for you. Signatures byte-match the cross-language golden vectors shared by the Go / Python / TypeScript SDKs.

Versioning

ContractVersion is the OpenAPI contract version this SDK targets. Construct any client and call Version() to read it.

License

Apache-2.0

Documentation

Overview

Package clearing is the official Go SDK for the Clearing economic-principal (EPID) service. It composes self-contained primitives (resilient resolution, RSA request signing, and the canonical-kind taxonomy — all vendored under internal/) into three capability tiers with a uniform shape across the Go / Python / TypeScript SDKs:

L1 ResolveClient — read:     Resolve / GetByEPID / Kinds (cache + breaker)
L2 SourceClient  — register: Ensure / Link / Affiliate (auto request signing)
L3 UnifyClient   — unify:    ProveKey / SubmitVerifiedAttr / Bind / LinkRealm

The tier is the permission boundary: constructing L2 or L3 requires a source identity and a private key, so an ordinary read-only consumer cannot obtain the write/unify handles from the type system alone.

The SDK hides the signing details the wire protocol exposes: the three signing headers (X-Clearing-Source / X-Clearing-Signature / X-Clearing-Timestamp), base64(std) encoding, Ed25519 vs RSA usage, the challenge fetch->sign->submit flow, and the rule that an attribute assertion's verifier_sig is signed over the request body excluding the verifier_sig field itself.

Index

Constants

View Source
const (
	RelationMemberOf       = "member_of"
	RelationAccountableFor = "accountable_for"
)

Relation enumerates economically-neutral affiliation edges.

View Source
const ContractVersion = "1.0.0"

ContractVersion is the OpenAPI contract major.minor.patch this SDK targets. Integration tests compare it against the live server info.version for compat.

Variables

View Source
var (
	// ErrNotRegistered: the principal/identity is authoritatively absent (404).
	// Re-exported from epidclient so L1 and L2/L3 share one sentinel.
	ErrNotRegistered = epidclient.ErrNotRegistered
	// ErrUnavailable: clearing is unreachable / circuit open / 5xx — not silently
	// masked; the caller decides fail-open vs fail-closed.
	ErrUnavailable = epidclient.ErrUnavailable
	// ErrPermission: source not authorized for this identity, or admin/governance
	// gate rejected (401/403).
	ErrPermission = errors.New("clearing: permission denied")
	// ErrConflict: unification/registration conflict (409) — identity already
	// mapped, challenge expired/used, binding not proven, low-tier auto-merge.
	ErrConflict = errors.New("clearing: conflict")
	// ErrInvalid: semantically invalid request (400/422) — bad body, unknown
	// kind/relation, self-merge, malformed key.
	ErrInvalid = errors.New("clearing: invalid request")
	// ErrRateLimited: too many challenges / requests (429).
	ErrRateLimited = errors.New("clearing: rate limited")
)

Stable SDK error sentinels. Consumers branch on these (errors.Is) to choose fail-open/closed; the SDK never silently fabricates a fallback.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	Status int    // HTTP status code
	Code   string // server "error" envelope code (e.g. not_registered)
	Detail string // optional human detail (500 only)
	// contains filtered or unexported fields
}

APIError carries the server's stable machine code + HTTP status alongside the classified sentinel, so consumers can log precisely while branching coarsely.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Is

func (e *APIError) Is(target error) bool

Is lets errors.Is(err, ErrPermission) match the classified sentinel.

type AttrAssertion

type AttrAssertion struct {
	EPID          string
	AttrType      string // phone | email | gov_id ...
	SaltedHash    string
	AssuranceTier int
	Method        string
}

AttrAssertion is a verifier's deduplication assertion for a strong attribute. The plaintext never leaves the verifier; only a salted hash is transmitted. Sig is filled by the SDK (RS256 over the canonical body, base64-std) — callers never set it.

type ClearingClient

type ClearingClient struct {
	L1 *ResolveClient // always present
	L2 *SourceClient  // nil unless constructed with a source key
	L3 *UnifyClient   // nil unless constructed with a source key
}

ClearingClient is the unified facade. Which tiers are non-nil depends on the constructor used, so capability is bound to the construction credential:

NewReadOnly  -> L1 only
NewSource    -> L1 + L2
NewUnify     -> L1 + L2 + L3

func NewReadOnly

func NewReadOnly(baseURL string, opt Options) *ClearingClient

NewReadOnly builds an L1-only client. Ordinary read-only consumers use this; the type system denies them L2/L3 (both stay nil).

func NewSource

func NewSource(baseURL, sourceID string, priv *rsa.PrivateKey, opt Options) *ClearingClient

NewSource builds an L1+L2 client for an authenticated event source. It requires the source identity + RSA private key.

func NewUnify

func NewUnify(baseURL, sourceID string, priv *rsa.PrivateKey, opt Options) *ClearingClient

NewUnify builds a full L1+L2+L3 client for a unification orchestrator. It requires the source identity + RSA private key (used for the verified-attr verifier_sig and realm-link request signing).

func (*ClearingClient) Version

func (c *ClearingClient) Version() string

Version returns the OpenAPI contract version this SDK targets.

type DedupResult

type DedupResult struct {
	ActiveEPID  string
	Merged      bool
	NeedsReview bool
}

DedupResult is the outcome of a unification (key-proof / verified-attr / binding). NeedsReview is only meaningful for verified-attr (low-tier hits).

type Ensured

type Ensured struct {
	EPID          string
	CanonicalKind string
	Created       bool // true=newly created, false=idempotent hit
}

Ensured is the ensure (idempotent adopt) result.

type Identity

type Identity struct {
	AuthInstanceID string // issuing auth instance (used as the source id on signed writes)
	Kind           string // external kind (user/agent/client/realm/provider...)
	Key            string // stable principal key within that auth instance
}

Identity is the external identity natural key (the realm is not part of the key).

type Options

type Options struct {
	// L1 resilience (forwarded verbatim to epidclient.Options).
	TTL              time.Duration
	NegativeTTL      time.Duration
	FailureThreshold int
	OpenTimeout      time.Duration

	// HTTPClient is used by L2/L3 (and L1's HTTP backend). Defaults to a client
	// with WriteTimeout. Injected so tests can stub the transport.
	HTTPClient *http.Client
	// WriteTimeout bounds L2/L3 calls when HTTPClient is not supplied (default 5s).
	WriteTimeout time.Duration
	// Now is an injectable clock (timestamps + resilience). Defaults to time.Now.
	Now func() time.Time
}

Options tunes both L1 resilience (forwarded to epidclient) and the HTTP transport shared by L2/L3. Zero values get industrial defaults.

type ResolveClient

type ResolveClient struct {
	// contains filtered or unexported fields
}

ResolveClient is the L1 read tier. It is a thin facade over epidclient.Client (cache + single-flight + circuit breaker), so it inherits the resilience and degradation semantics implemented there.

func (*ResolveClient) GetByEPID

func (c *ResolveClient) GetByEPID(ctx context.Context, epid string) (Resolved, error)

GetByEPID fetches the active principal for an EPID (follows merges). It bypasses the identity cache (different key space) and calls GET /v1/principals/:epid.

func (*ResolveClient) Invalidate

func (c *ResolveClient) Invalidate(id Identity)

Invalidate drops the cached resolution for an identity (after a known merge/link, callers clear the hot entry).

func (*ResolveClient) Kinds

func (c *ResolveClient) Kinds(ctx context.Context) (map[string]string, error)

Kinds returns the authoritative external→canonical kind mapping (cached). On unreachable server it falls back to the compiled-in canonicalkind table and records that the result is degraded (see kinds.go).

func (*ResolveClient) Resolve

func (c *ResolveClient) Resolve(ctx context.Context, id Identity) (Resolved, error)

Resolve maps an external identity to its active EPID (following merges). Returns ErrNotRegistered (safe to treat as absent) or ErrUnavailable (do not silently fabricate) per the resilience contract.

type Resolved

type Resolved struct {
	EPID          string
	CanonicalKind string
	Status        string // ACTIVE | MERGED | SUSPENDED (resolve always follows to active)
}

Resolved is the active-principal projection returned by resolve/getByEPID.

type SourceClient

type SourceClient struct {
	// contains filtered or unexported fields
}

SourceClient is the L2 registration tier. Only an authenticated event source constructs it, because it requires the source identity + RSA private key. Every write is source-signed automatically.

func (*SourceClient) Affiliate

func (c *SourceClient) Affiliate(ctx context.Context, subjectEPID, relation, targetEPID string) error

Affiliate writes an economically-neutral relation (member_of / accountable_for) between two principals (source-signed).

func (*SourceClient) Ensure

func (c *SourceClient) Ensure(ctx context.Context, id Identity) (Ensured, error)

Ensure idempotently adopts an external identity as an economic principal and returns its EPID. body.auth_instance_id must equal the signing source (server enforces, else ErrPermission). Safe to retry (idempotent + re-sign).

func (c *SourceClient) Link(ctx context.Context, id Identity, targetEPID string) error

Link attaches an unregistered identity to an existing EPID (source-signed).

func (*SourceClient) RotateKey

func (c *SourceClient) RotateKey(s requestSigner)

RotateKey swaps the signing private key (key rotation; ensure is idempotent so re-signing after rotation is safe).

type UnifyClient

type UnifyClient struct {
	// contains filtered or unexported fields
}

UnifyClient is the L3 unification tier. It orchestrates the challenge fetch->sign->submit flow and hides the Ed25519/RSA + base64(std) details. It holds the source RSA key (for the verified-attr verifier_sig and realm-link request signing) — so, like L2, only an authenticated source/orchestrator can construct it.

func (*UnifyClient) Bind

func (c *UnifyClient) Bind(ctx context.Context, subjectEPID, targetEPID string, sk, tk ed25519.PrivateKey) (DedupResult, error)

Bind performs a dual-binding merge: it starts a binding challenge, has both sides sign it with their Ed25519 keys, and submits the proofs. Both keys must already be registered active anchors for their EPIDs (via ProveKey first).

func (*UnifyClient) LinkRealm

func (c *UnifyClient) LinkRealm(ctx context.Context, orgEPID string, realm Identity, adminKey ed25519.PrivateKey) error

LinkRealm projects an org realm identity into the org EPID. The whole request is source-signed by the realm's source (realm.AuthInstanceID must equal the signing source), and admin control is proven by signing the canonical org-admin message with the org's registered Ed25519 admin key.

func (*UnifyClient) ProveKey

func (c *UnifyClient) ProveKey(ctx context.Context, epid string, edPriv ed25519.PrivateKey) (DedupResult, error)

ProveKey deduplicates by key control: it fetches a key-proof challenge, signs it with edPriv, and submits public key + fingerprint + signature. Same key across sources => same principal. No source assertion is involved.

func (*UnifyClient) SubmitVerifiedAttr

func (c *UnifyClient) SubmitVerifiedAttr(ctx context.Context, a AttrAssertion) (DedupResult, error)

SubmitVerifiedAttr submits a verifier's deduplication assertion. The SDK signs the canonical assertion body (WITHOUT verifier_sig — that field is json:"-" on the server) with the source RSA key and attaches it base64(std) as verifier_sig (closing the base64 footgun the protocol exposes).

Directories

Path Synopsis
internal
canonicalkind
Package canonicalkind is the single authoritative taxonomy of economic principal kinds (defined by Clearing, one source of truth).
Package canonicalkind is the single authoritative taxonomy of economic principal kinds (defined by Clearing, one source of truth).
contract
Package contract is the Go reference implementation for the cross-language golden vectors.
Package contract is the Go reference implementation for the cross-language golden vectors.
epidclient
Package epidclient is the resilient EPID resolution client.
Package epidclient is the resilient EPID resolution client.
sourcesign
Package sourcesign implements source-signed write authentication: deterministic canonical JSON plus RS256 (RSA-PKCS1v15 + SHA-256) sign/verify.
Package sourcesign implements source-signed write authentication: deterministic canonical JSON plus RS256 (RSA-PKCS1v15 + SHA-256) sign/verify.

Jump to

Keyboard shortcuts

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