Authentication
ChatWalaʻau has two complementary auth mechanisms: a unified API key (Bearer token) and an optional web sign-in (username/password) for cloud deployments.
Unified API key
API_KEY is a single Bearer token that protects the external OpenAI API, every
write REST endpoint, and the AG-UI chat stream when reached from a non-loopback
(LAN) client. Same-machine clients (127.0.0.1, ::1, localhost) bypass auth,
so localhost development stays zero-configuration even when APP_HOST=0.0.0.0.
API_KEY=sk-chatwalaau-your-secret-key-here
# APP_REQUIRE_AUTH_ON_LAN=true # default: fail-closed on LAN without a key
Decision matrix for write endpoints and the AG-UI chat stream (POST /ag-ui/):
| Client address | APP_REQUIRE_AUTH_ON_LAN | API_KEY | Outcome |
|---|---|---|---|
| loopback | any | any | allow |
| LAN | false | any | allow (operator opt-out) |
| LAN | true | empty | 503 |
| LAN | true | set | Bearer required |
/v1/responses always requires a matching Bearer key regardless of client address.
If APP_HOST is non-loopback and API_KEY is unset, the AG-UI stream now returns
the same 503 / 401 as every other write endpoint. Add API_KEY=..., or accept LAN
exposure explicitly with APP_REQUIRE_AUTH_ON_LAN=false.
Web SPA authentication (optional)
For deploying ChatWalaʻau as a private cloud web app where a single operator signs
in through the browser. It coexists with API_KEY (still used for CLI / SDK
access) and is disabled by default -- without AUTH_USERNAME there is no
behavior change.
AUTH_USERNAME=admin
AUTH_PASSWORD_HASH=scrypt$N=16384,r=8,p=1$<base64-salt>$<base64-hash>
# AUTH_SESSION_TTL_SECONDS=86400 # default 24h, sliding
# AUTH_COOKIE_SECURE=auto # auto / true / false
# AUTH_COOKIE_NAME=chatwalaau_session
Generate the hash with the bundled CLI:
chatwalaau hash-password # interactive (confirms twice)
echo "$PASSWORD" | chatwalaau hash-password --stdin --quiet # scripted
When AUTH_USERNAME is set, the SPA renders a /login page; the server validates
credentials in constant time and issues an opaque token via an HttpOnly +
SameSite=Strict cookie. The backend then accepts either a Bearer API_KEY
or a valid session cookie on every write endpoint and the AG-UI stream. The
/v1/responses external-app path stays Bearer-only.
- No new Python dependency (stdlib
hashlib.scrypt+secrets) - Single-user model; process-local session store (restarts re-prompt)
- HTTPS strongly recommended for non-loopback deployments
- Loopback CLI calls keep their no-credential bypass