Eanna banner — a lapis-blue gate with golden lions, light spilling through the open door

Portfolio — the platform

EANNA

one login for every private app — the front door asks Eanna before a request reaches anything

Eanna is the owner’s own code, written for one household and its friends; there is no public sign-up and no public login page. It is the successor to Shambala, the platform behind the previous site. This page describes the design, not the deployment.

What it is

Every private application on this server — the games console today, more tomorrow — needs to know who is asking and whether they may. Eanna answers both questions once, in one place, so that no application has to carry its own accounts, passwords, lockouts or two-factor flows. An app that wants to be protected declares a single grant name; members who hold that grant get in, everyone else gets a polite refusal.

The architecture

  1. 01

    Accounts

    Usernames, passwords hashed with a memory-hard function, roles (admin or member), a disable switch, and optional two-factor codes from any authenticator app.

  2. 02

    Sessions

    Server-side rows, not signed tokens: the browser holds an opaque, host-only, secure cookie; the server can revoke any session instantly.

  3. 03

    The check

    The front door asks Eanna about every request to a protected app. Eanna answers with the user’s name, id and groups — or with a redirect, or a refusal.

  4. 04

    Tickets

    A login on the sign-in host becomes a login on the app’s host through a single-use ticket: sixty seconds, one host, one redemption, return address allowlisted.

  5. 05

    Grants

    One row per member and app. Adding a protected app is one grant name and one front-door block — nothing inside the app changes.

  6. 06

    Audit

    Every login, failure, lockout, ticket, refusal and grant change is a row the owner can read; the admin surface is a command-line tool, deliberately.

How a login travels from the sign-in page to an app on a different hostname without a cookie that spans the whole domain: a one-time ticket, valid for a minute, bound to the exact host it was issued for, redeemed once. The public site never receives a cookie of any kind.

Sessions are rows, not signatures. The previous platform issued one signed cookie that every service read with a shared secret — simple, and exactly the kind of secret that ends up in five places. Eanna was written so that no service holds a secret at all: the front door asks, Eanna answers, and the answer is only trusted where nothing else can reach.

The dashboard. After signing in, a member sees cards for exactly the apps they hold a grant for, plus the public pages; an administrator sees everything. Adding a project to the platform is one registry row and one grant.

The application trusts the identity headers only because it has no other door. Strip them at the front, set them only from Eanna’s answer, and refuse the request if they are missing — so a misconfigured front door fails closed instead of open. That invariant is a unit test.

Feature highlights

  • One login

    Sign in once; every private app you hold a grant for opens without another password.

  • Lockout

    Five failures lock the account for a while; the correct password does not help until the clock runs out.

  • Two-factor, optional

    Enrol with a QR code, confirm with a code, disable with password and code.

  • Host-only cookies

    No cookie is valid anywhere but the host it was set on; the public site gets none at all.

  • Instant revocation

    Disable a member and every session they had is gone on the next request.

  • Self-service security

    Change your password (other sessions signed out), manage two-factor, sign out everywhere.

  • Read-only admin view

    Accounts, grants and the app registry at a glance; mutations stay on the command line for now.

  • Tested invariants

    Lockout, expiry, ticket reuse, wrong-host tickets, open-redirect attempts and the fail-closed check run before every deployment.

How it was built

Eanna was specified before it was built: the contract of the first protected app was written and reviewed as a document, then implemented against a test list, and it grew its login surface only on private hostnames, by rule.

The dashboard came second, ported from the previous site’s member area onto the same design — server-rendered this time, with no client script beyond a menu toggle.

Tech stack

  • Python
  • FastAPI
  • PostgreSQL
  • Alembic
  • Argon2
  • TOTP
  • Jinja templates
  • Caddy forward authentication
  • pytest
  • Claude (planner/executor loop)

Closing note

Eanna — one login for one household. No sign-up, no public login page — deliberately.