Skip to content

Security

Security that feels like hospitality.

An identity provider is only as good as the walls between its tenants and the care it takes with secrets. Here is how Usher is built. We describe what the software does; we make no claims of certification.

Architecture

Separate domains, separate networks.

Usher’s own pages and every customer’s issuer live on different registrable domains. The databases sit on a network that cannot reach the internet, and the one way out is a proxy that checks where it is going.

  • Caddy terminates TLS for every host and strips headers that only Usher’s own services may set.
  • The auth tier answers for every environment, told apart by host; nothing is cached for one host and served to another.
  • Each service talks to Valkey as its own user, limited to its own keys. There is no default user.
  • Statements carrying tokens and codes are never written to the database log with their values.

The internet

People, your apps, your servers

The edge

Caddy: TLS, HSTS, strips internal headers

usher.hallelo.net

Site and Console

usher.hallelo.net, console.usher.hallelo.net

API

api.usher.hallelo.net

*.id.hallelo.net

Auth tier

Every environment’s issuer and Lobby

Data network · no route out

Postgres

A schema per environment

Valkey

Each service limited to its own keys

The only way out

Egress proxy

For every customer-supplied URL

Discovery, JWKS, logout

Public addresses only

Usher’s tiers. The tenant domain is a different registrable domain from the platform’s, so a tenant’s pages are never same-site with Console.

Isolation

Nothing shared that matters.

Each project environment gets its own Postgres schema, its own signing keys and its own secrets sealed with its own data key. Its issuer is a host on the tenant domain, and its cookies are host-only, so a browser never sends one environment’s cookie to another.

usher.hallelo.net

The site, Usher Console, Usher accounts

*.id.hallelo.net

One issuer per environment, on a separate domain

Environment

Users, sessions and tokens live in a schema made for this environment. Queries run against that schema, never a shared table with a tenant column.

Copy Acme production’s sealed Google client secret into Northwind production.

Encryption at rest

Envelope encryption, bound to its place.

Secrets you give Usher are sealed with AES-256-GCM under a data key that belongs to one environment. Data keys are themselves wrapped by a versioned master key held outside the database.

Per-environment data keys
Compromising one environment’s data key reveals nothing about another’s.
AAD binding
The environment and the purpose are bound into every ciphertext, so a value can’t be moved between rows or tenants.
Versioned keys
Each sealed value records the key version it was made with, so keys can be rotated and old values rewrapped.
Authenticated
GCM’s tag means a tampered value fails to open, rather than decrypting to something else.
  1. Master key

    Held outside the database, versioned

  2. Data key, one per environment

    Wrapped by the master key

  3. The secret

    Sealed with AES-256-GCM under the data key

u1.<key version>.<iv>.<ciphertext + tag>

aad = u1 | version | environment | purpose

A sealed value names its environment and purpose in its authenticated data. Copied anywhere else, it fails to open.

Practices

The details, done carefully.

  • Passwords, if you use them

    Hashed with Argon2id at OWASP’s recommended cost (19 MiB, two passes), with a cap on how many run at once so a flood of sign-ins can’t take the service down. New passwords are checked against known breaches through Have I Been Pwned’s range API, which sees only a short prefix of the password’s hash. The minimum length is 12 by default.

  • Nothing stored that could be replayed

    Client secrets, tokens, magic-link tokens and emailed codes are stored hashed. Backup codes are encrypted. An authorisation code lasts five minutes; an emailed code allows three tries.

  • Rate limits on every door

    Sign-in, sign-up, emailed codes, magic links, password resets and second-factor checks each have their own limit: ten password sign-ins a minute, for example, and five emailed codes every ten minutes.

  • A strict Content-Security-Policy

    Every Lobby page gets a fresh nonce and a policy built around it: only this origin’s scripts carrying the nonce run, forms post only to the same origin, and other sites can’t frame the page.

  • An audit log you can trust

    Changes to workspaces, projects and environments are written to an append-only log, each entry hash-chained to the one before, so an edited or missing entry shows.

  • An egress proxy for customer URLs

    Every request Usher makes to an address a customer gave it (OIDC discovery, JWKS, back-channel logout) leaves through one proxy. It resolves the name once, refuses private, loopback, link-local and cloud-metadata addresses, and connects to the address it checked, so DNS rebinding can’t turn a customer URL into an internal one.

  • Handles that can’t pass for someone else

    A production environment’s handle is the first label of its issuer. Handles that read as Usher, a sign-in service or a well-known brand (look-alike digits included) are refused, and a handle once used is never given out again, because old apps may still trust its tokens.

  • Sessions that end when you say

    Usher checks its own sessions on every request rather than trusting a cached copy, so a session you end is over at once. Access tokens are short-lived (ten minutes by default), and your API can introspect them.

Responsible disclosure

Found something? Tell us first.

If you think you’ve found a security problem in Usher, we’d like to hear about it, and we’ll work with you to fix it.

security@usher.hallelo.net
  • What you found, where, and how to reproduce it.
  • What someone could do with it, as you understand it.
  • How we can reach you, and whether you’d like to be credited.

Please test only against environments you own, don’t access other people’s data, and give us a reasonable time to fix the problem before you share it. To report a page pretending to be an Usher sign-in, see Report abuse.