Security
The public Sandbox is open. A production EvidentSource deployment requires authentication. Bearer callers receive grants from their tokens; verified ALB-OIDC console users receive grants from an explicit server policy.
Read this whole page before relying on it. With EVS_REQUIRE_AUTH on, the server verifies tokens itself, or trusts a layer in front of it only when told to; the sections below say how to deploy safely.
Turning authentication on
Section titled “Turning authentication on”The server’s EVS_REQUIRE_AUTH setting decides whether a request must carry an identity. The AWS Marketplace template sets it from its RequireAuth parameter, which defaults to true.
With authentication off, the HTTP API allows every request that carries no credential. Such a request acts as an anonymous caller holding every permission. That is how the Sandbox runs, and it is never appropriate for real data. A request that does carry a token the server refuses, malformed or expired, is refused with 401 even then, rather than served anonymously. Authorization: Bearer with an empty token counts as no credential.
Authentication
Section titled “Authentication”A caller authenticates with a JSON Web Token:
- HTTP:
Authorization: Bearer <token> - gRPC: the
authorizationmetadata key, with the same value.
How tokens are verified
Section titled “How tokens are verified”With EVS_JWKS_URI set, the server verifies each token’s signature itself against the key set your identity provider publishes, and checks its issuer, audience and expiry. Configure it with:
| Setting | What it is |
|---|---|
EVS_JWKS_URI | The provider’s key set: the jwks_uri of its OpenID configuration |
EVS_JWT_ISSUER | The iss every token must carry |
EVS_JWT_AUDIENCE | An audience a token must name; separate several with commas |
EVS_JWKS_CA_FILE | Optional: a PEM file of your CA, if the provider’s certificate chains to a private one |
The default audience contract checks aud as a string or array. Set
EVS_JWT_AUDIENCE_MODE=cognito-access for Cognito access tokens: the server
requires a signed scalar client_id matching EVS_JWT_AUDIENCE and
token_use=access. Use a dedicated Cognito app client for this service;
configure its user pool as the issuer and keep grants least privileged.
ID tokens, wrong clients and tokens missing these claims are refused.
Signature, issuer, expiry, subject and not-before verification remain enforced.
The aud-string and aud-array modes additionally require the selected claim shape.
In the Marketplace template, set JwtAudienceMode to cognito-access and
JwtAudience to that app client ID. For ordinary aud tokens, choose
aud-string or aud-array (the existing default). Both ALB listeners and
the server check the same claim shape and value. This template accepts exactly
one printable ASCII audience without commas or whitespace; standalone server audience lists
remain supported. There is no setting to disable audience verification.
Whenever EVS_JWKS_URI is set, the server fetches the key set at startup, retrying for up to about a minute, and won’t start if it can’t, so it needs a network route to the identity provider. In the Marketplace template, these come from JwtJwksEndpoint, JwtIssuer and JwtAudience (one audience), which are required when RequireAuth is true. The load balancer verifies tokens on its HTTPS and gRPC listeners too. Requests to the MCP hostname skip the load balancer’s check, but the MCP service forwards the caller’s token, and the server verifies it.
With EVS_REQUIRE_AUTH on, the server won’t start unless something verifies tokens. It needs EVS_JWKS_URI, an ALB OIDC grants policy, or EVS_TRUST_EDGE_IDENTITY=true, which tells it to trust what a layer in front of it asserts: tokens that layer verified, and the identity headers an AWS load balancer sets after an OIDC sign-in. Set edge trust only when every request reaches the server through that layer. Otherwise anyone who can reach the server can write their own token. With EVS_JWKS_URI set as well, bearer tokens are still verified. Without a grants policy, trusted ALB headers identify a caller but assign no grants.
ALB OIDC console users
Section titled “ALB OIDC console users”For a console behind an ALB authenticate-oidc or authenticate-cognito action,
set EVS_REQUIRE_AUTH=true and EVS_ALB_OIDC_GRANTS_FILE to an operator-owned
JSON file available inside each server. For example:
{ "alb_arn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/console/0123456789abcdef", "issuer": "https://your-oidc-issuer.example", "client_id": "your-console-app-client", "subjects": { "the-users-exact-oidc-subject": { "databases": { "production": ["reader"] } } }}Replace the ARN, issuer, client and subject with your actual OIDC deployment
values. Use the identity provider’s stable sub, rather than matching email or
groups. The server verifies the signature of x-amzn-oidc-data, its ALB signer,
issuer, client and signed expiry, then grants only the listed roles. Unlisted
users receive no grants and see an empty catalog. The unsigned identity header
and any user-info grant claims cannot grant access.
databases accepts reader, writer, deployer and database_deleter on exact
database names. A subject can explicitly use global: ["database_creator"] or
all_databases: ["reader"], but add those only when that broader scope is
needed. Unknown fields, roles and roles in the wrong scope fail startup.
Commercial AWS and GovCloud ALBs are supported. The server needs outbound HTTPS
access to AWS’s regional ALB public-key endpoint. Restrict target ingress to your
ALB and configure its authenticate action on every console route. Signature
verification supports this mode without EVS_TRUST_EDGE_IDENTITY. If API clients
also use bearer tokens, configure EVS_JWKS_URI and its issuer/audience settings;
bearer tokens are verified first. Without JWKS or explicit edge trust, a presented
bearer token is refused.
Distribute the policy through your deployment’s protected configuration mount or image and set the file path in its server environment. The stock Marketplace template configures JWT verification; it does not create an OIDC login action or mount this file. Customize the deployment for OIDC console sign-in.
Policy changes require restarting every server. Updated nodes apply the new grants on the next request, including with an existing ALB session. Existing requests keep their captured identity; server-streaming gRPC RPCs stop at signed token expiry or server shutdown. HTTP streamed presentations are authorized when the request is admitted. Private State View responses cached by a browser can remain visible for up to a minute. No grants policy is enabled by default.
Keep the load balancer internal (AlbScheme, the default), so that only callers inside your network can reach it at all. An internal load balancer limits who can connect; it doesn’t make a token trustworthy.
Authorization
Section titled “Authorization”A token’s evs:grants claim describes what the caller may do. Grants name roles, either globally, for named databases, or for every database:
{ "evs:grants": { "global": ["database_creator"], "databases": { "orders": ["reader", "writer"], "orders-staging": ["reader", "writer", "deployer", "database_deleter"] }, "all_databases": ["reader"] }}| Role | Scope | Permits |
|---|---|---|
database_creator | global | Creating databases |
reader | database | Querying events, rendering State Views |
writer | database | Everything reader permits, plus appending transactions and running State Changes |
deployer | database | Publishing State Changes and State Views, and expiring a State View’s snapshots |
database_deleter | database | Deleting the database |
Roles combine: a caller holds the union of every role it is granted. deployer doesn’t include reader.
Where grants are enforced
Section titled “Where grants are enforced”With authentication on, every HTTP route and gRPC call that names a database checks the caller’s grants on it:
- reading anything (events, the log, transactions, State Views, listings, definitions, and the web pages that show them) needs
reader; - appending a transaction or running a State Change needs
writer; - publishing a State Change or State View, or expiring a State View’s snapshots, needs
deployer; - deleting a database needs
database_deleter, and creating onedatabase_creator.
The database catalog lists only the databases a caller holds a grant on. A caller with no grant on a database gets 403 (gRPC PERMISSION_DENIED) for anything that names it. One exception is inherent: creating a database whose name is taken answers 409 (gRPC ALREADY_EXISTS), so a caller allowed to create databases can learn that a name is in use.
Streaming and revoked grants
Section titled “Streaming and revoked grants”Every authenticated gRPC stream ends at its token’s expiry with
UNAUTHENTICATED. Reconnect with a fresh token; the server checks its grants again.
Expiry releases an idle or backpressured stream’s source. The terminal status
arrives when transport capacity permits. Data already sent or buffered cannot
be recalled.
With authentication required, a streaming credential must include an expiry;
a trusted-edge identity without one cannot open a stream.
Bearer grants are carried in signed tokens. Removing a grant at your identity provider does not invalidate tokens already issued: their grants remain usable until expiry, including on an open stream. Issue short-lived tokens to bound that revocation window. The server does not poll your provider for grant changes.
Verified ALB-OIDC grants come from the policy loaded at startup. After changing that policy and restarting every server, new requests use the updated grants, even with the same still-valid ALB token. Open streams retain their opening grants until token expiry or server shutdown.
Identity
Section titled “Identity”The server takes the caller’s identity from the token:
subis the caller’s subject.evs:principaldescribes the caller:name,email,type(human,agentorsystem),provider, andupstream_id. For an agent,delegatornames the human it acts for, with asubjectand aname.- Without
evs:principal, the server readsnameandemailfrom the token’s top level.
What gets recorded
Section titled “What gets recorded”- The caller’s attributes — subject, name, email, issuer, principal type and delegator — are recorded on transactions appended directly, and on State Changes run synchronously, over HTTP or gRPC.
- Every event carries
authtype, the kind of caller, and for an identified caller itsauthid. - Asynchronous commands record the caller too: the server writes the authenticated caller onto the record it produces. Kafka’s access control is the trust boundary: a record produced to the command topic directly records whatever caller attributes it carries, so grant produce rights on
{database}.{database_id}.async-commandonly to producers you trust to say who they act for. - With authentication off, a caller over HTTP is not identified: its events carry
authtypeunauthenticated, and a State Change sees an unauthenticated caller. Over gRPC, a caller that presents a token is still recorded as itself.
On an event, authtype, authid and the other auth attributes come from the caller’s credentials, or from the record an asynchronous command carries; the server discards any copy a client puts on the event itself. They are context, not proof: they aren’t signed, and they record who the credentials or the record said the caller was, so read authid together with authtype. See Decision Traces & AI-Readiness.
With telemetry enabled, HTTP and gRPC request traces record the admitted
caller’s enduser.id and optional enduser.email and auth.provider.
These request spans are emitted at INFO and survive the default info filter.
auth.source identifies the credential path: bearer, alb_oidc, or
authenticated for a custom extractor. A token’s provider claim cannot change
that source. Optional requests without identity record anonymous; missing
required identity or a credential the server cannot admit records refused.
An issuer outage still returns UNAVAILABLE/503, rather than declaring the
token invalid. Bearer attribution follows the verification posture configured
above; optional unverified or edge-trusted identity is not independent signature
proof. Credentials and raw token claims are omitted from these trace fields.
gRPC health and reflection bypass authentication; gRPC health request traces are
filtered, and reflection traces carry no authentication verdict. HTTP health
probes also bypass authentication and retain their request traces.
Storage credentials
Section titled “Storage credentials”The server needs no secrets of its own. Its access to S3 comes from the IAM role its tasks run with, which the Marketplace template creates. Don’t configure static AWS keys.
Before going to production
Section titled “Before going to production”RequireAuthistrue, andJwtIssuer,JwtJwksEndpointandJwtAudienceare set.- The load balancer is
internal, unless it must be reachable from outside your network. - Each caller’s token grants only the databases and roles it needs.
- The TLS certificate in ACM covers the API hostname.
- You watch the traces and metrics the template’s collector sends to AWS X-Ray and CloudWatch.