@sarek/core
TypeScript client for the Sarek Core Service. Zero runtime dependencies, ESM, Node.js 18 or later. It handles hashing, signing, file storage and HTTP.
npm install @sarek/coreinitSarekCore()
Creates a SarekCore instance. Loads an existing identity from disk if one
exists, but never registers one automatically.
Signature
function initSarekCore(options?: InitOptions): Promise<SarekCore>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
options.name | string | 'default' | Agent name. Used for the local identity and proof store. |
options.url | string | from sarek-core/config.json | Core Service endpoint. |
options.timeout | number | 30000 | HTTP timeout in milliseconds. |
options.retries | number | 3 | Retries on 5xx and network errors. 4xx is never retried. |
options.persist | boolean | true | false disables all file system access. |
Returns
Promise<SarekCore> with the methods documented below.
Example
import { initSarekCore } from '@sarek/core'
const sarek = await initSarekCore()stamp()
Hashes data locally with SHA3-256, stamps the hash, and stores the record and proof on disk. The call blocks until the proof is anchored (around 10 seconds).
The method is overloaded: a single input returns a single StampResult, an
array returns StampResult[]. A batch is stamped in one transaction.
Signature
function stamp(data: string | Buffer, options?: StampOptions): Promise<StampResult>
function stamp(data: (string | Buffer)[], options?: StampOptions): Promise<StampResult[]>Parameters
| Name | Type | Description |
|---|---|---|
data | string | Buffer | (string | Buffer)[] | The data to stamp. A 64-character hex string is treated as a precomputed hash and passed through. A Buffer is always hashed. |
options.metadata | Record<string, unknown> | Optional context stored in the local record. |
options.signResult | { signature, publicKey, salt } | Optional signature to attach, produced by sign(). |
Returns
interface StampResult {
id: string // generated UUID
hash: string // SHA3-256 of the input
ldgp: string // base64-encoded proof file
txHash: string
blockNumber: number
timestamp: string // ISO 8601
metadata?: Record<string, unknown>
}Example
const data = 'Patient 4821: high risk for diabetes'
const proof = await sarek.stamp(data, {
metadata: { type: 'risk-assessment', patientId: '4821' }
})Errors
Throws LedgerError on HTTP failures and TimeoutError if the Core Service
does not answer within timeout.
verify()
Verifies a proof. Recognizes proof input by its TERHUA prefix (the base64
encoding of the proof file magic bytes), so argument order does not matter.
Passing a SignResult instead verifies a signature. See
Verification for what each check means.
Signature
function verify(input: string | SignResult, data?: string):
Promise<VerifyProofResponse | VerifySignatureResponse>Parameters
| Name | Type | Description |
|---|---|---|
input | string | SignResult | A base64 proof, raw data (if the proof is in data), or a SignResult. |
data | string | Optional original data, enables the data integrity check. |
Returns
interface VerifyProofResponse {
valid: boolean
treeIntegrity: boolean
onChain: boolean
onChainLeaf?: boolean
blockNumber?: number
blockDate?: string
}Example
// Proof only
const r1 = await sarek.verify(proof.ldgp)
// Proof plus original data, in either order
const r2 = await sarek.verify(proof.ldgp, 'loan #4821 approved')
const r3 = await sarek.verify('loan #4821 approved', proof.ldgp)Errors
Throws LedgerError if neither argument is a proof.
register()
Opt-in identity. Generates a P-256 key pair, registers it with the Core
Service, and persists it under sarek-core/id/<name>/. Call it once per agent.
Signature
function register(): Promise<void>Example
await sarek.register()Errors
Throws IdentityExistsError (HTTP 409) if the identity is already registered.
sign()
Signs data locally with the registered identity. Runs offline in well under a millisecond.
Signature
function sign(data: string): SignResultReturns
interface SignResult {
message: string
signature: string // hex
publicKey: string // PEM
salt: string // hex
}Example
const sig = sarek.sign('loan #4821 approved')
const check = await sarek.verify(sig)Errors
Throws IdentityNotRegisteredError if register() has not been
called. This is a local state error, not an HTTP error.
revoke()
Revokes the registered identity. Irreversible.
Signature
function revoke(): Promise<void>Example
await sarek.revoke()Errors
Throws IdentityRevokedError (HTTP 409) if already revoked, and
IdentityNotRegisteredError if no identity exists locally.
getIdentity()
Returns the locally stored identity, or null if none exists.
Signature
function getIdentity(): StoredIdentity | nullReturns
interface StoredIdentity {
identityId: string
publicKey: string // PEM
salt: string // hex
commitment: string
}Example
const id = sarek.getIdentity()Error classes
All errors are exported from @sarek/core.
| Class | Meaning |
|---|---|
LedgerError | Base class. Carries status, path and body. |
IdentityNotRegisteredError | sign() or revoke() called before register(). Local, not HTTP. |
IdentityExistsError | 409 from register(): identity already registered. |
IdentityRevokedError | 409 from revoke(): identity already revoked. |
NotFoundError | 404: resource does not exist. |
TimeoutError | 408: no answer within the configured timeout. |
import { initSarekCore, IdentityNotRegisteredError } from '@sarek/core'
const sarek = await initSarekCore({ name: 'invoice-stamper' })
try {
sarek.sign('loan #4821 approved')
} catch (err) {
if (err instanceof IdentityNotRegisteredError) {
await sarek.register()
} else {
throw err
}
}Local files
With persist: true (the default) the SDK maintains a sarek-core/ directory
in the working directory:
sarek-core/
config.json url, timeout, retries
.gitignore created automatically, protects private.key
records/
records.jsonl one line per stamp
proofs/
<name>.ldgp append-only proof store per agent name
id/
<name>/
identity.json publicKey, identityId, commitment
private.key P-256 private key (chmod 600)
index.json offset and size per proof id, O(1) lookup