Authenticated gateway, challenge service, risk composer, and SAS issuer for Entros.
The executor does not contain proprietary behavioral models. It forwards validation requests to a separate, private validation HTTP service.
- Authenticate integrators and enforce quotas.
- Issue and consume challenge nonces.
- Apply request, wallet, and IP rate limits.
- Forward feature summaries and phrase audio to the private validator.
- Combine returned risk signals under the configured policy.
- Issue best-effort SAS attestations for eligible wallet flows.
- Expose health and aggregate operational metrics.
The private validator performs feature checks, phrase transcription, acoustic analysis, and cross-wallet fingerprint comparison.
| Route | Auth | Purpose |
|---|---|---|
GET /challenge |
API key | Issue a server challenge and nonce |
POST /validate-features |
API key | Forward capture evidence to the private validator |
POST /attest |
API key and wallet proof | Issue a SAS attestation when configured |
GET /paired/authorization |
API key | Issue the values a wallet signs for paired sessions |
POST /challenge/paired |
API key and wallet authorization | Open a paired session when paired sessions are enabled |
POST /paired/cue |
API key | Release the outstanding round cue |
POST /paired/commit |
API key | Commit one paired round when paired sessions are enabled |
POST /validate-session |
API key and wallet authorization | Finalize a paired session when paired sessions are enabled |
POST /study/definition |
Private API key | Return the study definition when the study routes are enabled |
POST /study/enrol |
Private API key | Enrol a study participant when the study routes are enabled |
GET /health |
Public | Return service health |
GET /status |
Optional API key | Return status publicly and detailed metrics to callers with a key that is not public |
GET /metrics |
Public | Return aggregate Prometheus counters |
Wallet clients submit their own protocol transactions. The executor relays none.
/attest writes the on-chain verification time into the attestation. It issues nothing for a
wallet that received its identity by migration and has not verified since. In the same
transaction it closes the attestations this service issued to earlier holders of the identity,
up to three wallets back, when those wallets hold no identity.
/validate-features accepts an optional challenge_nonce: the 32-byte array that /challenge
returned, in the same JSON form. A request that names it is checked against exactly that
challenge, from any client network. Send it with every capture. A request without it is checked
against the latest challenge its own client network asked for, or, when that network asked for
none, against the wallet's latest challenge. A signed request must name the nonce its wallet
authorization covers. A nonce of another length, or one that differs from the signed nonce,
answers 400.
A paired session runs three rounds of one word and one short path. The validator holds the
round state, so this service keeps none. The current source requires paired protocol version 2 at open.
Cue responses bind the outstanding round. The validator enforces round and cue expiry.
Open, cue and commit omit the finalize timing floor. The finalize
request goes through the same timing floor, quota and admission checks as
/validate-features. The paired routes answer 404 until EXECUTOR_PAIRED_ENABLED is set.
A paired open and a paired finalize must show that the wallet's owner asked for them.
-
The client calls
GET /paired/authorization?wallet=<base58>. -
The executor answers
challenge,expires_at,expires_in,originandmac. -
The wallet signs this UTF-8 text, with
\nline ends and no trailing newline:Entros verification Sign to start verification sessions for this wallet. The authorization expires within the hour. It moves no funds and approves no transaction. Entros-Paired-Open-v1 wallet:<base58> challenge:<hex> expires:<expires_at> -
The client sends
authorizationwith each open and finalize. The object holds the five answered values, withoutexpires_in, andsignature_hex.
Build the signed text from the fields. Do not sign text that a server supplies whole.
One authorization serves every session until it expires. expires_in gives its lifetime in
seconds, so a client can time it without the device clock. A lifetime is 300 seconds at least,
because clients drop an authorization with under 120 seconds left. A finalize can come from
another network, and the executor accepts it up to one session lifetime after the expiry.
EXECUTOR_PAIRED_AUTHORIZATION_NETWORK decides an open from another client network than the
one that asked for the authorization. enforce refuses it. observe serves it and counts it in
entros_paired_authorization_network_mismatch_total. Failed attempts are charged as follows:
enforce: every request with a valid authorization is charged to the wallet.observe: a request with a valid authorization is charged to the wallet when it comes from the network that asked for the authorization. Otherwise it is charged to the wallet as seen from the network it came from.
The executor answers 401 with reason authorization_required or authorization_invalid.
On either reason, the client asks for a new authorization.
EXECUTOR_PAIRED_AUTHORIZATION=optional serves a request that carries no authorization.
Use it during a client rollout only. Roll out in this order:
- Set
EXECUTOR_PAIRED_AUTHORIZATION=optionalandEXECUTOR_PAIRED_AUTHORIZATION_NETWORK=observe. - Ship the clients that send an authorization.
- Set
EXECUTOR_PAIRED_AUTHORIZATION=required. - Set
EXECUTOR_PAIRED_AUTHORIZATION_NETWORK=enforceonceentros_paired_authorization_network_mismatch_totalstays near zero.
Paired rounds show that the client fixed each round's evidence before the next round was revealed. They show nothing more about where that evidence came from.
Finalize uploads are bounded in total and per client network. A finalize that has read its
body and passed its authorization check then takes one of EXECUTOR_PAIRED_SESSION_CONCURRENCY
processing slots. A request that finds no free place or
slot within two seconds answers 503 with reason session_busy. Nothing was consumed, so the
client sends the same finalize again. A finalize body must arrive within 20 seconds.
- Client network.
EXECUTOR_PER_IP_RATE_LIMIT_PER_MINapplies to one client network: an IPv4 address or an IPv6 /64. An IPv6 /48 has a second limit at 16 times that rate. Every network in that /48 shares it, and it matters only once the service has IPv6 ingress. - API key.
RATE_LIMIT_PER_MINUTEapplies to each key that is not public./attesthas a second limit of 10 requests per minute for each such key. - Public key. Mark every key that ships in a browser or an app with
"public": trueinINTEGRATORS. The client-network limit then applies to its requests, and no holder shares a limit with another. A public key must have quota0, it cannot use the study routes, and/statusanswers it as an anonymous caller. - Attestation. Each wallet can attest twice per minute. The executor counts a request after it checks the wallet signature.
- Attempts.
VALIDATION_WALLET_MAX_ATTEMPTScounts failed validations. A request that carries a valid wallet signature counts against the wallet. Any other request counts against the wallet as seen from its client network.
EXECUTOR_SINGLE_CAPTURE_UPDATES=refuse makes /validate-features answer 409 with reason
paired_required for each request. It needs EXECUTOR_PAIRED_ENABLED=true. The route refuses
each baseline write in both modes.
cp .env.example .env
cargo build
cargo test
cargo runThe example configuration uses ENVIRONMENT=dev. Without VALIDATION_SERVICE_URL, feature requests use an insecure local pass-through.
That mode supports interface development only. It does not perform Entros behavioral validation.
Debug builds use a neutral scoring policy when EXECUTOR_SCORING_CONFIG_BUNDLE
is absent. Every release build requires a valid signed bundle.
Install the script dependencies with npm ci before signing a policy. The signer uses the installed tsx binary.
Keep the policy, authority keypair, and bundle under ../.config. The signer rejects private artifacts inside this public worktree.
ENVIRONMENT accepts only dev or prod. Unknown values stop startup.
Production requires:
- At least one explicit
INTEGRATORSentry. - A private
VALIDATION_SERVICE_URL. - A valid
VALIDATION_SERVICE_URL_SIGNATURE. - A valid
EXECUTOR_SCORING_CONFIG_BUNDLE. - At least one valid
CORS_ORIGINSentry. - A dedicated SAS authority when SAS credential fields are configured.
EXECUTOR_PAIRED_AUTHORIZATION_KEY_HEXwhen paired sessions are enabled.
Production refuses the dev validator pass-through and permissive CORS mode.
| Variable | Default | Purpose |
|---|---|---|
ENVIRONMENT |
dev |
Exact runtime mode: dev or prod |
RPC_URL |
Solana devnet | Solana RPC endpoint |
WS_URL |
Solana devnet | Solana WebSocket endpoint |
RELAYER_KEYPAIR |
unset | Relayer keypair JSON |
RELAYER_KEYPAIR_PATH |
./relayer-keypair.json |
Relayer keypair path |
LISTEN_ADDR |
0.0.0.0:3001 |
Local bind address when PORT is absent |
PORT |
unset | Platform-provided listen port |
API_KEYS |
[] |
Development API-key list |
INTEGRATORS |
[] |
Named API keys with explicit quotas and the optional public flag |
RATE_LIMIT_PER_MINUTE |
60 |
Request limit for each key that is not public |
EXECUTOR_PER_IP_RATE_LIMIT_PER_MIN |
30 |
Request limit for each client network |
CORS_ORIGINS |
[] |
Exact allowed HTTP origins |
VALIDATION_SERVICE_URL |
unset | Private validator endpoint |
VALIDATION_SERVICE_URL_SIGNATURE |
unset | Authority signature over the validator URL |
VALIDATION_API_KEY |
unset | Credential sent to the private validator |
EXECUTOR_SCORING_CONFIG_BUNDLE |
unset | Signed scoring configuration required by release builds |
CHALLENGE_TTL_SECS |
180 |
Challenge nonce lifetime in seconds. Allowed range: 1-300. |
VALIDATION_WALLET_MAX_ATTEMPTS |
5 |
Failed attempts allowed per wallet window |
VALIDATION_WALLET_WINDOW_SECS |
3600 |
Wallet attempt window |
SAS_CREDENTIAL_PDA |
unset | SAS credential address |
SAS_SCHEMA_PDA |
unset | SAS schema address |
SAS_AUTHORITY_KEYPAIR |
unset | Dedicated SAS authority JSON |
SAS_AUTHORITY_KEYPAIR_PATH |
unset | Dedicated SAS authority path |
SAS_ATTESTATION_TTL_DAYS |
30 |
Attestation lifetime |
EXECUTOR_AUTOMATION_OBSERVE |
true |
Record bounded automation telemetry |
EXECUTOR_AUTOMATION_WEBDRIVER_REJECT |
true |
Reject reported WebDriver sessions when validation runs |
EXECUTOR_WALLET_REPUTATION_OBSERVE |
true |
Record public wallet signals |
EXECUTOR_CURVE_TRACE_OBSERVE |
true |
Record bounded curve-trace telemetry |
VALIDATION_CROSS_WALLET_COOLDOWN_SECS |
86400 |
Cross-wallet cooldown duration |
VALIDATION_CROSS_WALLET_COOLDOWN_ENFORCE |
false |
Enforce the cooldown when enabled |
EXECUTOR_PAIRED_ENABLED |
false |
Serve the paired-session routes |
EXECUTOR_PAIRED_SESSION_CONCURRENCY |
16 |
Paired finalize requests processed at once. Allowed range: 1-128. |
EXECUTOR_PAIRED_AUTHORIZATION |
required |
required or optional. Whether a paired open and finalize must carry a wallet authorization. |
EXECUTOR_PAIRED_AUTHORIZATION_KEY_HEX |
unset | 32 random bytes as 64 hex characters. The executor signs each authorization with it. Unset draws a key at start. |
EXECUTOR_PAIRED_AUTHORIZATION_NETWORK |
enforce |
enforce or observe. Whether a paired open from another client network than the authorization's is refused. |
EXECUTOR_PAIRED_AUTHORIZATION_TTL_SECS |
900 |
Authorization lifetime in seconds. Allowed range: 300-3600. |
EXECUTOR_SINGLE_CAPTURE_UPDATES |
allow |
allow or refuse. Whether /validate-features serves updates. refuse needs paired sessions. |
EXECUTOR_STUDY_ROUTES_ENABLED |
false |
Serve the study routes |
Do not place keypairs or API credentials in source control.
cargo fmt --all --check
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo test --locked --all-features
cargo build --locked --release
cd scripts
npm ci
npm run test:sign-scoringMIT.
VALIDATION_IDENTITY_PROGRAM_ID selects the program used to read validation identity state. The default is the official Anchor program.
An alternate program requires a loopback listener, Solana devnet, and ENVIRONMENT=prod. Signed scoring and validator URL checks still apply.
The gateway derives receipt intent from the selected on-chain identity. Client receipt flags do not override that decision.
Authenticated GET /validation-deployment returns the identity program, validation-only mode, and challenge requirement.
Alternate mode excludes /attest and the study routes.