Authentication

All API requests require an API key passed via the X-API-Key header.

X-API-Key: your_api_key_here

Request an API key from the Get API Key page. Free tier keys are provisioned within 24 hours.

The GET /health endpoint does not require authentication.

Base URL

All endpoints are served from:

https://api.starksentinel.com

Health Check

GET /health

Returns the current status of the SENTINEL API. No authentication required.

Example Request

curl https://api.starksentinel.com/health

Example Response

{
  "status": "ok",
  "version": "1.3.1",
  "service": "sentinel-attestation-service"
}

Response Fields

FieldTypeDescription
statusstringService status. "ok" when operational.
versionstringCurrent API version.
servicestringService identifier.

Generate Proof

POST /v1/prove

Generates a STARK proof attesting that a set of behavioral observations stays within defined EWMA control limits. The proof is zero-knowledge: raw observations are used to compute the EWMA in-memory, then discarded. Only public inputs (observation count, final EWMA, control limits, pass/fail) are included in the proof envelope.

Request Body

FieldTypeRequiredDescription
observationsnumber[]YesArray of behavioral metric values (unsigned integers). Minimum 2, maximum 1024 observations.
baseline_meannumberYesExpected baseline mean for the EWMA calculation.
uclnumberYesUpper Control Limit. EWMA must stay below this value to pass.
lclnumberYesLower Control Limit. EWMA must stay above this value to pass.

Example Request

# Generate a STARK proof for 8 behavioral observations
curl -X POST https://api.starksentinel.com/v1/prove \
  -H "X-API-Key: $SENTINEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "observations": [95, 98, 92, 97, 100, 94, 96, 99],
    "baseline_mean": 96,
    "ucl": 110,
    "lcl": 80
  }'

Example Response

{
  "version": "1.2.0",
  "proof_bytes": [4, 0, 0, 4, ...],
  "public_inputs": {
    "num_observations": 8,
    "first_observation": 95,
    "final_ewma": 95,
    "lambda_num": 1,
    "lambda_den": 5,
    "baseline_mean": 96,
    "ucl": 110,
    "lcl": 80,
    "within_limits": true
  },
  "metadata": {
    "trace_length": 16,
    "num_constraints": 3,
    "security_level": 97,
    "generation_time_ms": 2,
    "proof_size_bytes": 5995
  },
  "chain_hash": "72f2dd09...8b0c33"
}

Response Fields

FieldTypeDescription
versionstringProof format version (currently "1.2.0").
proof_bytesnumber[]STARK proof as a byte array (integers 0–255). Pass the entire proof envelope to /v1/verify.
public_inputs.num_observationsnumberCount of observations included in the proof.
public_inputs.first_observationnumberThe first observation in the measurement window.
public_inputs.final_ewmanumberFinal EWMA value after processing all observations.
public_inputs.lambda_num / lambda_dennumberEWMA smoothing factor as a fraction (1/5 = λ 0.2).
public_inputs.baseline_meannumberThe baseline mean used for EWMA seeding.
public_inputs.uclnumberUpper Control Limit used in the proof.
public_inputs.lclnumberLower Control Limit used in the proof.
public_inputs.within_limitsbooleantrue if EWMA stayed within [lcl, ucl] for all observations.
metadata.trace_lengthnumberNumber of rows in the STARK execution trace.
metadata.num_constraintsnumberNumber of algebraic constraints in the AIR.
metadata.security_levelnumberConjectured security level in bits.
metadata.generation_time_msnumberTime to generate the proof in milliseconds.
metadata.proof_size_bytesnumberSize of the proof in bytes.
chain_hashstringBlake3 hash of this proof (64-char hex). Use as previous_proof_hash in the next proof to build a chain.

Privacy Guarantee

Individual observation values are never included in the response. The proof attests to the statistical properties (EWMA within control limits) without revealing the underlying data. This is the Prove-Don't-Share architecture.

Verify Proof

POST /v1/verify

Verifies a previously generated STARK proof. Verification confirms that the proof was correctly generated and that the public inputs are authentic. Verification is always free and does not count against your monthly proof limit.

Request Body

Send the complete proof envelope — the entire JSON response from /v1/prove. All fields are required.

FieldTypeRequiredDescription
versionstringYesProof format version from the prove response.
proof_bytesnumber[]YesSTARK proof byte array from the prove response.
public_inputsobjectYesThe public_inputs object from the prove response.
metadataobjectYesThe metadata object from the prove response.
chain_hashstringYesThe chain_hash from the prove response.

Example Request

# Save the prove response, then pass it directly to verify
curl -s -X POST https://api.starksentinel.com/v1/prove \
  -H "X-API-Key: $SENTINEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"observations": [95,98,92,97,100,94,96,99], "baseline_mean": 96, "ucl": 110, "lcl": 80}' > proof.json

# Verify the full proof envelope
curl -X POST https://api.starksentinel.com/v1/verify \
  -H "X-API-Key: $SENTINEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d @proof.json

Example Response

{
  "valid": true
}

Response Fields

FieldTypeDescription
validbooleantrue if the proof is cryptographically valid.

Client-Side Verification

For zero-trust verification, use the open-source WASM verifier. It runs entirely client-side — no network calls, no trust in SENTINEL required. See the Quickstart Guide for setup instructions.

Verify Proof Chain

POST /v1/verify-chain

Verifies a chain of linked STARK proofs. Checks that each proof is cryptographically valid and that chain links are intact — each proof's previous_proof_hash matches the preceding proof's chain_hash. The first proof in the chain must have a null previous hash (genesis proof).

Request Body

A JSON array of proof envelopes, ordered from genesis (first) to most recent (last). Maximum 100 proofs per request.

FieldTypeDescription
[].proof_bytesnumber[]Serialized STARK proof (byte array).
[].public_inputsobjectEWMA public inputs from the prove response.
[].chain_hashstringBlake3 hash of this proof (64-char hex).
[].previous_proof_hashstring|nullBlake3 hash of the previous proof, or null for genesis.
[].metadataobjectProof metadata (security level, generation time, etc.).
[].versionstringProof format version.

Example Request

# Verify a 2-proof chain
curl -X POST https://api.starksentinel.com/v1/verify-chain \
  -H "X-API-Key: $SENTINEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    { "version": "1.2.0", "proof_bytes": [...], "public_inputs": {...},
      "chain_hash": "a1b2c3...", "previous_proof_hash": null, "metadata": {...} },
    { "version": "1.2.0", "proof_bytes": [...], "public_inputs": {...},
      "chain_hash": "d4e5f6...", "previous_proof_hash": "a1b2c3...", "metadata": {...} }
  ]'

Example Response

{
  "valid": true,
  "chain_length": 2,
  "valid_proofs": 2,
  "valid_links": 2,
  "failed_proofs": [],
  "broken_links": []
}

Response Fields

FieldTypeDescription
validbooleantrue if all proofs verify and all links are intact.
chain_lengthnumberTotal number of proofs in the chain.
valid_proofsnumberCount of individually valid STARK proofs.
valid_linksnumberCount of valid chain links (hash continuity checks).
failed_proofsnumber[]Indices of proofs that failed verification.
broken_linksnumber[]Indices where chain link continuity is broken.

Aggregate Proofs

POST /v1/aggregate

Aggregates a chain of proofs into a single Merkle commitment. Verifies all proofs and chain links, then produces a compact aggregate with a Merkle root, summary statistics, and per-proof summaries. Maximum 10,000 proofs per request.

Request Body

Same format as /v1/verify-chain — a JSON array of proof envelopes ordered from genesis to most recent.

Example Response

{
  "version": "1.0.0",
  "chain_id": "my-agent-chain",
  "chain_length": 2,
  "merkle_root": "f8a3b1c9d2e4...",
  "aggregate_inputs": {
    "first_observation": 95,
    "final_ewma": 97,
    "all_within_limits": true,
    "min_security_level": 128,
    "total_observations": 16
  },
  "proof_summaries": [
    {
      "chain_hash": "a1b2c3...",
      "within_limits": true,
      "num_observations": 8,
      "final_ewma": 95,
      "security_level": 128
    }
  ],
  "metadata": {
    "aggregation_time_ms": 1,
    "proofs_verified": 2,
    "links_verified": 1,
    "aggregated_at": "2026-04-03T12:00:00.000Z"
  }
}

Response Fields

FieldTypeDescription
merkle_rootstringBlake3 Merkle root committing to all proofs in the chain.
aggregate_inputs.all_within_limitsbooleantrue if every proof in the chain passed its control limits.
aggregate_inputs.total_observationsnumberSum of observations across all proofs.
aggregate_inputs.min_security_levelnumberMinimum security level across the chain (weakest link).
proof_summariesarrayPer-proof summary with chain hash, EWMA, and status.
metadata.aggregation_time_msnumberTime to aggregate and verify in milliseconds.
chain_idstring?Optional chain identifier. Assigned after creation for chain grouping and registry queries.

Verify Aggregate

POST /v1/verify-aggregate

Verifies a previously produced aggregate by recomputing the Merkle root from the proof summaries and confirming it matches. This is a lightweight check — no STARK proofs need to be re-verified.

Request Body

Pass the full aggregate response object from /v1/aggregate.

Example Response

{
  "valid": true
}

Response Fields

FieldTypeDescription
validbooleantrue if the Merkle root matches the proof summaries.
errorstringPresent only if valid is false. Describes the mismatch.

Proof Registry API

The Proof Registry is a separate service at registry.starksentinel.com. It provides immutable, publicly queryable storage for proof records.

Registry Endpoints

MethodPathAuthDescription
POST/v1/proofsRequiredRegister a proof. Returns 201 on success, 409 if already registered.
GET/v1/proofs/:chain_hashPublicLook up a single proof by its chain hash.
GET/v1/proofsPublicList proofs with pagination. Filter by publisher or chain_id.
GET/v1/chains/:chain_idPublicRetrieve all proofs in a chain, ordered by registration time.
GET/healthPublicRegistry health check with capacity monitoring.

Base URL

https://registry.starksentinel.com

Register a Proof

# Register a proof after generating and verifying it
curl -X POST https://registry.starksentinel.com/v1/proofs \
  -H "X-API-Key: $SENTINEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chain_hash": "a1b2c3d4e5f6...",
    "previous_proof_hash": null,
    "version": "1.2.0",
    "publisher": "my-company",
    "chain_id": "agent-quality-chain",
    "num_observations": 8,
    "final_ewma": 95,
    "within_limits": true,
    "ucl": 110,
    "lcl": 80,
    "baseline_mean": 96,
    "security_level": 128,
    "proof_size_bytes": 6761
  }'

Look Up a Proof

# Anyone can look up a registered proof — no API key needed
curl https://registry.starksentinel.com/v1/proofs/a1b2c3d4e5f6...

Example Lookup Response

{
  "id": 1,
  "chain_hash": "a1b2c3d4e5f6...",
  "previous_proof_hash": null,
  "version": "1.2.0",
  "publisher": "my-company",
  "chain_id": "agent-quality-chain",
  "num_observations": 8,
  "final_ewma": 95,
  "within_limits": true,
  "ucl": 110,
  "lcl": 80,
  "baseline_mean": 96,
  "security_level": 128,
  "proof_size_bytes": 6761,
  "registered_at": "2026-04-03T12:00:00.000Z"
}

Registration is immutable — once a proof is registered, it cannot be modified or deleted. Attempting to register a duplicate chain_hash returns 409 Conflict.

MCP Tools

SENTINEL ships as an MCP server with 11 tools for direct integration into AI agent frameworks — including 3 dedicated SRTAP tools for real-time attestation.

ToolDescription
sentinel_prove_behaviorGenerate a STARK proof from behavioral observations via the SENTINEL API
sentinel_verify_behaviorCryptographically verify a single STARK proof
sentinel_verify_chainVerify an ordered chain of linked proofs
sentinel_aggregate_chainAggregate a proof chain into a single Merkle commitment (up to 10,000 proofs)
sentinel_verify_aggregateVerify an aggregate proof by recomputing the Merkle root
sentinel_compute_bidCalculate Behavioral Injection Score locally (0-100)
sentinel_sanitize_inputStrip invisible Unicode, reasoning tags, and model control tokens
sentinel_check_healthCheck SENTINEL Attestation Service availability
sentinel_srtap_configureSet behavioral barriers for real-time attestation (baseline, UCL, LCL, security level)
sentinel_srtap_observeSubmit observations and receive a chained STARK proof (auto-links via previous_proof_hash)
sentinel_srtap_statusQuery SRTAP session state — chain length, latest hash, config, aggregate readiness

Compute BID Score

MCP sentinel_compute_bid

Calculates a Behavioral Injection Score (BIS) locally by comparing 5 behavioral metrics against their baselines. Returns a composite 0-100 score, a response level, and per-metric sigma distances. Runs entirely in-process — no API call required.

Parameters

FieldTypeRequiredDescription
enforcementRatenumberYesRatio of blocked to total actions (0-1)
targetEntropynumberYesShannon entropy of action targets
actionFrequencynumberYesActions per minute
typeConcentrationnumberYesRatio of dominant action type (0-1)
latencyMsnumberYesEnforcement processing time in ms
*_meannumberYesBaseline mean for each metric (e.g. enforcementRate_mean)
*_stdDevnumberYesBaseline standard deviation for each metric

Example Request

# Via MCP tool call
{
  "enforcementRate": 0.35,
  "enforcementRate_mean": 0.05,
  "enforcementRate_stdDev": 0.02,
  "targetEntropy": 0.8,
  "targetEntropy_mean": 2.1,
  "targetEntropy_stdDev": 0.3,
  "actionFrequency": 45,
  "actionFrequency_mean": 12,
  "actionFrequency_stdDev": 3,
  "typeConcentration": 0.9,
  "typeConcentration_mean": 0.4,
  "typeConcentration_stdDev": 0.1,
  "latencyMs": 15,
  "latencyMs_mean": 5,
  "latencyMs_stdDev": 2
}

Example Response

{
  "bis": 82.4,
  "response_level": "PAUSE",
  "metrics": {
    "enforcementRate": { "value": 0.35, "sigma": 15.0, "weight": 0.35 },
    "targetEntropy": { "value": 0.8, "sigma": -4.33, "weight": 0.25 },
    "actionFrequency": { "value": 45, "sigma": 11.0, "weight": 0.20 },
    "typeConcentration": { "value": 0.9, "sigma": 5.0, "weight": 0.15 },
    "latencyMs": { "value": 15, "sigma": 5.0, "weight": 0.05 }
  }
}

Response Levels

LevelBIS RangeAction
NORMAL0 – 25No action. Baseline behavior.
ENHANCED25 – 50Increased logging and monitoring.
ALERT50 – 75Alert operators. Restrict sensitive operations.
PAUSE75 – 90Pause agent execution. Require human review.
TERMINATE90 – 100Terminate session immediately.

Sanitize Input

MCP sentinel_sanitize_input

Strips invisible Unicode characters, reasoning tags, and model control tokens from input text. Returns the cleaned text with a breakdown of what was removed. Runs locally — no API call required.

Parameters

FieldTypeRequiredDescription
textstringYesThe input text to sanitize

Example Response

{
  "cleaned": "Please summarize the quarterly report",
  "strippedCount": 14,
  "categories": {
    "tags_block": 0,
    "zero_width": 8,
    "bidi_control": 2,
    "reasoning_tags": 1,
    "model_control": 3
  }
}

What Gets Stripped

CategoryExamples
tags_blockUnicode Tags block (U+E0001–U+E007F) — invisible tag characters
zero_widthZero-width space (U+200B), zero-width joiner (U+200D), zero-width non-joiner (U+200C)
bidi_controlBidirectional overrides (U+202A–U+202E, U+2066–U+2069) — text direction manipulation
reasoning_tags<thinking>, <thought>, <antThinking> — reasoning process hijack attempts
model_control<|im_start|>, <|im_end|>, <|endoftext|> — model control token injection

Error Codes

SENTINEL uses standard HTTP status codes. Error responses include a JSON body with a message field.

CodeMeaningCommon Cause
400Bad RequestMissing or invalid fields in the request body. Check that observations is a non-empty array and ucl > lcl.
401UnauthorizedMissing or invalid X-API-Key header.
429Too Many RequestsMonthly proof limit exceeded for your tier. Upgrade your plan or wait for the next billing cycle.
500Internal Server ErrorUnexpected server error. Retry with exponential backoff. If persistent, contact support.

Error Response Format

{
  "error": "Bad Request",
  "message": "observations must be a non-empty array of numbers"
}

Rate Limits

Rate limits are based on your plan tier and apply to proof generation only. Verification calls and health checks are unlimited.

TierProofs per MonthRequests per Second
Free1005
Pro5,00050
EnterpriseUnlimitedCustom

When you exceed your monthly limit, subsequent /v1/prove calls return 429 Too Many Requests. Rate limit headers are included in every response:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 2026-05-01T00:00:00Z

API Key Security

API keys authenticate your requests to the SENTINEL Attestation Service. Treat them like passwords.

Key Format

All keys follow the format sk-sentinel-{tier}-{random}. The prefix identifies the tier (free, pro, enterprise) but carries no special privileges beyond rate limits.

Key Provisioning

Free and Pro keys are provisioned instantly via the self-serve form. Enterprise keys require manual review within 24 hours. Keys are generated server-side and returned once — they cannot be recovered after the initial display. Store them securely.

Key Scoping

Each key is scoped to a single tier with fixed rate limits. Keys cannot be used to access endpoints beyond their tier's allowance. Verification endpoints (/v1/verify, /v1/verify-chain, /v1/verify-aggregate) are free and unlimited for all tiers.

Rotation

To rotate a key, request a new one via the API key form using the same email address. The old key remains active until you stop using it — SENTINEL does not enforce automatic expiry at this time. We recommend rotating keys every 90 days or immediately if you suspect compromise.

Revocation

To revoke a compromised key, email admin@forgewerke.com with the key prefix (first 20 characters). Revoked keys return 401 Unauthorized on all subsequent requests. Revocation takes effect within 5 minutes.

Expiry Policy

Keys do not currently expire automatically. Keys that have not been used for 180 days may be subject to deactivation with 30 days advance notice to the registered email. Enterprise keys follow custom retention policies agreed during onboarding.

Best Practices

Store keys in environment variables or a secrets manager — never commit them to source control. Use separate keys for development and production. Monitor your usage via the X-RateLimit-* response headers to detect unauthorized use. If a key is exposed in a public repository, rotate it immediately.