Identity your application owns. Not a server you have to trust.

SESAME is a headless authentication and authorization engine. It opens no port and ships no UI — your application owns every listener and drives the engine over a versioned protocol on stdin and stdout. Sessions, tokens, policy, and audit stay inside your own stack, in any of 10 languages.

go install github.com/d31ma/sesame/cmd/sesame@latest copy
✓ No network listener ✓ One direct dependency ✓ Default deny, always ✓ Apache-2.0
84
protocol operations
10
language SDKs
1
direct dependency
0
ports opened
What it does

One place for who, what, and why.

Principals, credentials, sessions, tokens, policy, and audit — with the decision boundary in the same engine as the identities it decides about.

Authenticate

Passkeys first, passwords properly

WebAuthn passkeys with sign-counter clone detection, Argon2id passwords with a parameter-upgrade path, TOTP, and single-use recovery codes. A code is spent durably, so a replay fails inside its own window and across a restart.

Authorize

Decisions you can explain

Default deny over roles, grants, groups, and bounded context conditions. Every decision returns a stable reason and the policy version it was made under, so an audit answers "why" and not just "no".

Single sign-on

OIDC provider, with PKCE mandatory

Authorization code flow, rotating refresh families that revoke the whole family on reuse, discovery, introspection, revocation, consent, and RP-initiated logout. There is no grant-types field, so implicit and password can never be switched on.

Federate

Inbound OIDC and SAML 2.0

Bring identities from an existing provider. SAML verification refuses every ambiguous document rather than choosing between readings, which is what defeats signature wrapping.

Provision

SCIM 2.0 users and groups

A directory can create, patch, and deprovision principals and group membership. Reconciliation is idempotent, because directories re-send full state rather than diffs.

Account for

A ledger, not a log file

Every security-relevant change is a hash-chained event on FYLO. Projections are rebuildable, snapshots are verified, and revocation survives a restart with a defined propagation bound.

How it fits

A subprocess, not a service.

Most identity servers ask you to run one more thing on the network and trust it with your users. SESAME is a binary your application starts. It has no listener to expose, no admin console to leak, and no origin to be confused about.

Your application Owns every listener, TLS boundary, route, and middleware chain. Nothing about that changes.
SDK shim One dependency-free file. Owns the subprocess, timeouts, cancellation, and typed errors — never a security decision.
SESAME engine NDJSON on stdin and stdout. Every protocol decision, every policy evaluation, every audit record.
FYLO Documents are authoritative. Indexes, caches, and projections are derived and rebuildable.

The architecture is enforced, not documented: a test inspects the linked dependency graph of the shipped binary and fails the build if an HTTP package, a template engine, or an unexpected module ever appears in it.

In your language

Ten languages, one engine.

Copy one file into your project. It spawns the engine, speaks the protocol, and gets out of the way. There is no registry to depend on and no lockfile entry to resolve — and with SESAME_DEPLOYMENT set, no path in your code either.

// SESAME_BINARY and SESAME_DEPLOYMENT come from the environment.
client, err := sesame.Start(ctx, sesame.Options{})
if err != nil {
    log.Fatal(err)
}
defer client.Close()

decision, err := client.Decide(ctx, sesame.DecisionRequest{
    TenantID:    tenantID,
    PrincipalID: principalID,
    Action:      "doc:read",
    Resource:    "project:alpha",
}, nil)

fmt.Println(decision.Decision, decision.ReasonCode)
// deny deny_no_grant
Features

Everything it does. And everything it does not.

A security project that overstates itself is worse than one that ships less. On the left is what exists — each of them with code, negative-path tests, operator documentation, and recovery evidence behind it. On the right is what does not exist yet, written down here rather than left for you to find out in production.

OAuth 2.0 / OIDC provider

Authorization code flow with mandatory PKCE, rotating refresh families with reuse detection, discovery, introspection, revocation, consent, and RP-initiated logout.

Device grant, PAR, and DPoP

RFC 8628 for inputs a browser cannot reach, RFC 9126 to move the authorization request onto an authenticated back channel, and RFC 9449 to bind an access token to a key its holder proves per request.

Authentication

Argon2id passwords with a parameter-upgrade path, TOTP with durable replay prevention, single-use recovery codes, and WebAuthn passkeys with clone detection.

Authorization

Deterministic default-deny decisions over roles, grants, groups, and bounded context conditions, with stable reasons and a versioned policy snapshot.

Federation and provisioning

Inbound OIDC federation, SCIM 2.0 users and groups, and inbound SAML 2.0 with an in-tree XML canonicalizer that refuses every ambiguous document.

Durability

A hash-chained security ledger on FYLO with rebuildable projections, verified snapshots, and restart evidence run against a real FYLO runtime.

Ten drop-in clients

One dependency-free file per language, each holding the whole operation surface. A contract test resolves every operation against every shim, so a capability cannot ship in one language and quietly miss another.

OpenID certification

The provider surface is driven over real TLS by a relying party in test/interop — discovery, PKCE, ID-token claims, single-use codes. Certification is self-certification against the OpenID Foundation suite, and that submission has not been made, so SESAME is not certified.

SAML interoperability

Proven end to end against a real Keycloak in test/interop — which is how a namespace defect no fixture had ever produced was found and fixed. Okta, Entra ID, Google Workspace, and Shibboleth are still unproven; treat any provider not listed as untested.

Production support

This is a developer preview. A repeatable restore, upgrade/rollback, and soak evidence runner exists, but no release has passed its 72-hour native gate or an independent security review. No version is supported for production use.

Every attack case in SESAME's adversarial suite runs against a real compiled binary over the shipped protocol, against a real deployment with real keys — because a defence that only exists inside a package boundary is not a defence.

Read the code before you trust it.

SESAME is Apache-2.0 and every security claim on this page points at a test you can run. Start with the project plan — it records what is proven, what is deliberately not, and why.

← Docs Reference

Reason and error codes

Every code SESAME can return, what it means, and what to do about it. These are a compatibility boundary: branch on the code, never on the message.

Decision reasons

Returned on every authorization decision alongside allow or deny. A decision is not an error — a denial is a successful call with a negative answer.

ReasonMeaning
allow_role_grant A role granted directly to the principal matched.
allow_group_grant A role granted to a group the principal belongs to matched.
deny_no_grant No applicable grant. This is the default answer, and also what a supplied-but-non-matching condition produces.
deny_principal_suspended The principal exists but is suspended. Checked before any grant.
deny_principal_not_found No such principal in this tenant.
deny_tenant_not_found No such tenant.
deny_missing_context A condition needed an attribute the request did not supply. Names the attribute only when every supplied value already matched — otherwise naming it would be false advice.
deny_session_invalid A session was supplied instead of a principal, and it is expired, revoked, or unknown. Never falls back to a lower assurance.

Protocol errors

Returned as {"ok": false, "error": {"code": ..., "message": ..., "retryable": ...}}. Your SDK raises these as a typed error carrying the code.

Transport and protocol

Raised by the machine protocol itself, before any operation runs.

CodeMeaning
invalid_json The request line was not a JSON object.
unsupported_protocol The request named a protocol version this engine does not speak.
operation_not_found This build does not route that operation. Usually a version mismatch; call RequireOperations at startup to find out sooner.
frame_too_large The request exceeded the 1 MiB frame limit.
internal_error An unexpected failure. Retryable only if the error says so.

Configuration

The engine is running but cannot serve this operation. Every one of these fails closed rather than guessing.

CodeMeaning
storage_not_configured No deployment or FYLO root. Set SESAME_DEPLOYMENT, or FYLO_BINARY and FYLO_ROOT together.
signing_not_configured Token issuance needs the deployment signing key.
issuer_not_configured Token issuance needs an issuer. Pass --issuer to sesame init.
secrets_not_configured Sealing needs the deployment secrets key. TOTP enrolment cannot proceed without it.
relying_party_not_configured Passkeys need a relying-party identifier and origin.

Tenants, principals, and identifiers

CodeMeaning
tenant_not_found No such tenant.
principal_not_found No such principal, or it belongs to another tenant.
identifier_conflict That normalised identifier is already claimed in this tenant. Claims are atomic, so exactly one concurrent creation wins.

Roles, grants, and groups

CodeMeaning
role_exists A role with that name already exists in the tenant.
role_not_found No such role.
grant_exists That role is already granted to that subject.
grant_not_found No such grant.
group_exists A group with that name already exists in the tenant.
group_not_found No such group.
group_member_exists That principal is already a member.
group_member_not_found That principal is not a member.
stale_policy_version A pinned policy version is older than the engine can still answer for.

Authentication and sessions

Note what is absent: there is no "unknown identifier" code, because beginning a login succeeds either way.

CodeMeaning
transaction_not_found No such authentication transaction, or it belongs to another tenant.
transaction_closed The transaction is completed, failed, or out of attempts. Start a new one.
session_not_found No such session.
session_inactive The session is expired or revoked.
totp_not_enrolled The principal has no activated TOTP authenticator.
totp_already_active TOTP is already activated for this principal.
totp_invalid_code The code is wrong, outside its window, or already spent. A spent code fails here even though it was valid moments ago.
passkey_exists That credential is already registered.
passkey_not_found No such passkey.
passkey_challenge_expired The registration or assertion challenge is no longer current.
passkey_rejected The assertion failed verification, including a sign counter that went backwards — which indicates a cloned authenticator.

OIDC clients and tokens

CodeMeaning
client_exists A client with that name already exists in the tenant.
client_not_found No such client.
client_disabled The client is disabled. Disablement is immediate and durable.
invalid_redirect_uri The redirect URI is not an exact registered match. Prefix and wildcard matching are not supported, deliberately.
scope_not_allowed The request asked for a scope the client is not registered for.
consent_required A third-party client needs recorded consent before it receives a code.
consent_not_found No standing consent for that principal and client.
interaction_not_found No such interaction.
interaction_closed The interaction is spent or expired.
invalid_grant Every token-exchange failure, undifferentiated on purpose.
refresh_family_not_found No such refresh-token family.
authorization_pending The device grant's user has not approved it yet. This is the only outcome that invites another poll; RFC 8628 spells it this way, so device libraries branch on it.
slow_down The device is polling faster than the interval it was issued. Add five seconds and keep going — guidance, not a refusal.
access_denied The device authorization was refused, expired, or ran out of user-code attempts. One code for all three, so a device cannot probe the verification surface through the token endpoint.
device_authorization_not_found No such device authorization.
user_code_not_found No pending device authorization for that user code, including one whose attempts are spent.
dpop_proof_invalid The DPoP proof is malformed: not a compact JWS, the wrong typ, an algorithm other than ES256, a key that is not a public P-256 point, or a signature that does not verify.
dpop_proof_not_bound The proof names a different HTTP method or URI than the one the host says it served, or its ath is for a different access token. A proof is good for one request and one token.
dpop_proof_expired The proof’s iat is outside its one-minute window, in either direction. A future-dated proof is refused as firmly as a stale one — a fast clock would otherwise mint proofs valid long after the moment they were meant for.
dpop_proof_replayed This proof identifier has already been used. The one DPoP failure that means something is wrong rather than that something is broken.
dpop_key_mismatch The proof is signed by a key other than the one the token is bound to — a valid proof, presented with somebody else’s token.
dpop_foreign_origin The proof names a URI outside this deployment’s issuer origin. This is the one binding check the engine makes without trusting the host, so a proof minted for another authorization server is refused even by a host that reported it faithfully.
dpop_required A key-bound refresh token was presented without a proof. Fail closed: a bound token used as a bearer token is exactly the theft the binding exists to catch.
request_uri_not_found The request_uri is unknown, expired, already spent, or belongs to another client. One code for all four: a pushed reference that told a caller which of those it was would be a probe of somebody else’s requests.
request_uri_conflict An authorization request carried both a request_uri and loose parameters. RFC 9126 forbids merging them, and SESAME refuses rather than ignores, so a client finds out instead of silently getting the pushed values.
invalid_logout_hint The id_token_hint is missing, malformed, or not one this deployment issued.
invalid_post_logout_redirect_uri The post-logout redirect URI is not registered for that client.

Federation, SCIM, and SAML

The two assertion codes cover every cause; the specific reason goes to the audit ledger.

CodeMeaning
provider_not_found Unknown, disabled, or cross-tenant OIDC provider.
provider_not_configured The provider has no validated metadata yet. Fetch its discovery document first.
provider_document_rejected A discovery document or key set failed validation.
federated_login_not_found Unknown or cross-tenant federated login.
federated_login_closed That federated login is already spent.
federated_login_expired That federated login has expired.
subject_not_linked A verified assertion for a subject no principal claims, under strict linking.
assertion_rejected An inbound OIDC assertion failed verification.
provisioning_client_not_found Unknown SCIM client or bearer token.
provisioning_denied The SCIM bearer token is missing, wrong, or belongs to a disabled client.
provisioning_forbidden The SCIM client is not granted the capability it used, such as group management.
scim_user_not_found No such provisioned user.
scim_user_conflict That userName is already provisioned.
scim_group_not_found No such provisioned group.
scim_unsupported A SCIM feature this engine does not implement, such as a value-path PATCH filter.
saml_provider_not_found Unknown, disabled, or cross-tenant SAML provider.
saml_login_not_found Unknown, closed, expired, or cross-tenant SAML login.
saml_subject_not_linked A verified assertion for a subject no principal claims, under strict linking.
saml_assertion_rejected A SAML assertion failed verification.

Request validation

CodeMeaning
invalid_request A parameter is missing, malformed, or out of range. The message describes the parameter and never a credential.

Codes that are deliberately vague

Three refusals collapse many causes into one code on purpose. A caller able to tell them apart would learn the shape of a security flow.

  • invalid_grant — every token-exchange failure: spent code, wrong verifier, mismatched redirect URI, unknown client.
  • assertion_rejected and saml_assertion_rejected — every federated assertion failure. The specific reason goes to the audit ledger instead.
  • Unknown identifiers at authn.begin — no error at all. Beginning succeeds regardless, so the endpoint cannot be used to enumerate accounts.

What to read next