Ansina’s REST API now has a real control surface: a dependency-isolated CLI for automation and a TUI for operators.
We built ansina-tui without importing ansina, then wired authentication, token lifecycle, raw API access, and a live Overview tab on top of HTTP. The result is a usable front door for both CI/CD and humans, with failure states designed instead of leaked as tracebacks.
⚡ A Separate Control Surface
-
Dependency isolation —
tui/owns its packaging, lockfile, virtual environment, and CI job.tests/unit/test_isolation.pypins the boundary: the CLI never imports the daemon package, whiletyper,httpx,textual, andrichstay on the client side. -
One invocation, two modes — Bare
ansina-tuiopens the TUI only on a TTY. Any argument selects the CLI, while a non-TTY bare invocation prints help to stderr and exits 2.--refreshis validated only when an actual TUI launch occurs. -
Operational exit codes —
statuschecks/healthzbefore/readyz, then/version, and distinguishes unreachable, not-ready, and unhealthy states. Codes 6 and 7 make the command useful as a container readiness and health probe, not just a diagnostic.
🔒 Identity and Credential Boundaries
-
Self identity is first-class —
GET /auth/meresolves the existingPrincipalwithout a database read. Theme.*policy carve-out grants every builtin role access tome.profile, while sensitiveauth.*routes remain protected. -
Tokens are self-service — Users can mint, list, and revoke their own tokens; administrators can manage tokens on behalf of other users. Raw credentials are returned exactly once, hashes never leave the server, and revoked tokens fail on the next authenticated request.
-
Bootstrap means break-glass — The bootstrap identity is auto-generated, never overridden or rotated, and can hold exactly one token. A separate configured Admin is provisioned from
ANSINA_SECURITY__ADMIN_USERNAMEandANSINA_SECURITY__API_TOKEN; the token-only configuration is no longer valid.
🧠 Automation Without Hidden Interaction
-
Raw REST coverage —
ansina-tui apireaches every daemon route without a client-side allow-list.-f key=valuebuilds simple JSON bodies,--inputsends typed or raw payloads, and-Hcannot override the realAuthorizationorX-Sudo-Tokenheaders. -
Sudo becomes state — A live sudo grant is attached automatically through the shared session layer. Without one, sensitive calls exit 4 with an
auth sudohint and never prompt mid-request, keeping the API command safe for CI/CD. -
Output stays machine-safe —
--jsonand piped output preserve the daemon body verbatim, including non-2xx problem documents. Diagnostics go to stderr, soapi ... --json | jqremains predictable.
🔬 A TUI That Fails Deliberately
-
Overview is a refresh contract —
fetch_overview()reads health, readiness, version, and identity into anOverviewSnapshot. Connectivity failures and insecurehosts.tomlbecome designed states rather than exceptions from Textual workers. -
No flicker, no duplicate widgets — An exclusive refresh worker updates existing sections in place on a timer or with
r. Adding another tab is an append toTABS, not a rewrite of the application shell. -
The honest boundary — Heart-disabled handling is explicitly out of scope because Overview calls four routes and
/heart/tickis not one of them. That acceptance item is N/A, documented rather than silently implied.
M4 is shipped: a REST API with a control surface that respects both operators and automation. ⚙️
#AIEngineering #SoftwareEngineering #CommandLineTools #MultiAgentSystems #RBAC