Blog

Engineering Database-Backed RBAC with Sudo Enforcement

September 4, 2026
ai-engineerai-agentView source PR ↗

Ansina’s access control is now database-backed, route-audited, and step-up protected.

This milestone replaced static credential assumptions with a complete RBAC system spanning identity, permissions, sudo grants, and management APIs. The hard part was not adding decorators. It was making every authorization decision explicit, testable, and difficult to bypass accidentally.

The Authorization Boundary

  • Database-backed identity — Users, groups, roles, credentials, external identities, and role assignments now live in SQLite repositories. API tokens are stored only as salted hashes, passwords use argon2id, and BearerAuthMiddleware resolves active principals through an injectable authenticator chain.

  • Startup as a security gateaudit_route_coverage() walks the real FastAPI route table and fails create_app() when a non-public route lacks require(...). The old BOOTSTRAP_RESOURCES list is gone; the running application is now the resource catalog.

  • Explicit failure semantics — Unauthorized requests remain 401, while insufficient permissions return distinguishable 403 problem+json codes. The response does not reveal whether the identity, token, or permission was the failing component.

⚙️ Sudo as a Real Step-Up Flow

  • Verifier-agnostic grantsSudoService owns issuance, TTL, revocation, and lockout above a StepUpVerifier protocol. M2 uses password re-verification, but a future verifier can be registered without rewriting grant or enforcement logic.

  • Sensitive routes are structurally protected — Maintain must present a live X-Sudo-Token for sensitive resources; Admin does not. Bad, expired, or revoked grants do not become authentication failures—they simply leave the principal without sudo and produce CODE_SUDO_REQUIRED.

  • State survives restarts — Sudo grants and lockouts are persisted in sudo_grants and sudo_lockouts, not process memory. Failed attempts produce a real 429 with Retry-After, and no raw password or grant token reaches logs.

🛡️ Management Without Self-Escalation

  • The API can bootstrap its own users — Admin can create a user over HTTP, set its password, issue a token, assign Maintain, and exercise the resulting identity. This is the first end-to-end path that provisions a non-bootstrap principal without touching SQLite directly.

  • Maintain is deliberately bounded — Even with a valid sudo grant, Maintain cannot assign Admin or any role granting auth.* permissions. The checks cannot be reduced to a permission-subset comparison because Maintain and Admin share fixed grants under this policy.

  • Deletion is a tombstoneDELETE /auth/users/{id} records deleted_at and purges credentials, assignments, memberships, and live sudo grants in one transaction. The identity remains for audit attribution, but manually reactivating the row restores nothing.

🔬 Verification Against the Failure Modes

  • Boot behavior is explicit — Fresh databases auto-generate a 256-bit bootstrap token, print it once, and retain only its hash. Operator overrides are validated for entropy, dev mode requires a loopback bind, and manual runs confirmed stable restart behavior with no secrets in JSON logs.

  • The suite reached 598 unit testsruff, formatting, and mypy --strict stayed clean while unit coverage remained at 100%. The e2e suite reached 20 black-box tests, including the full 401 → 403 → 200 authorization chain and the RBAC management round trip.

  • The failure cases are intentional — Missing route dependencies fail startup. Inactive or tombstoned users stop authenticating. The final Admin cannot be deleted or demoted, and a sudoed Maintain cannot mint an Admin.

Deterministic access control is now part of the application boundary, not a convention.

#AIEngineering #SoftwareEngineering #RBAC #AccessControl #MultiAgentSystems

Ansina: Heart & Brain — MLX heart runtime, autonomic tick loop, and OpenAI‑compatible brain

August 28, 2026
ai-agentai-a2aai-mcpView source PR ↗

Small changes in surface, big moves under the hood: Ansina gets a Heart and a Brain.

Lead: Merged PR #23 wires together three pieces that make Ansina more autonomous and interoperable — a Heart runtime protocol (MLX), an autonomic tick loop for continuous decisioning, and a BrainProvider with an OpenAI-compatible adapter. This is about runtime contracts, graceful looping, and making the brain easier to swap and test.

❤️ Heart: runtime protocol & MLX adapter

  • 🧭 Implemented a Heart runtime protocol (src/ansina/heart/runtime.py) to formalize how the agent’s “heart” signals intentions and reacts to runtime events.
  • 🔌 Added an MLX adapter (src/ansina/heart/adapters/mlx.py) so the Heart can speak an external ML execution layer — a practical runtime bridge for experiments and deployments.
  • 🛠️ Exposed heartbeat endpoints and route integration (src/ansina/api/routes/heart.py) so orchestration layers can observe and drive the Heart.
  • ✅ Tests added around models, runtime, and the MLX adapter (tests/unit/heart/* and tests/unit/heart/adapters/test_mlx.py) to keep the contract honest.

🧠 BrainProvider: port + OpenAI-compatible adapter

  • 🔁 Ported a BrainProvider abstraction (src/ansina/brain/provider.py) to centralize model calls, retries, and selection logic.
  • 🧩 Introduced an OpenAI-compatible adapter (src/ansina/brain/adapters/openai_compat.py) to make local or third-party LLMs plug into existing tooling and tests.
  • 🧪 Rich test coverage for events, retry logic, and adapter behavior (tests/unit/brain/*) to validate fallbacks and deterministic selection strategies.
  • 📦 Config surfaced in settings and examples (src/ansina/config/settings.py, ansina.example.toml) so switching brains is configuration, not code surgery.

⚙️ Autonomy: autonomic tick loop & decision snapshots

  • ⏱️ Built an autonomic tick loop (src/ansina/heart/tick/loop.py) that drives periodic decision ticks and lifecycle transitions — the agent keeps itself honest over time.
  • 🧠 Split decision logic, snapshots, and selection into focused modules (tick/decision.py, tick/snapshot.py) for easier reasoning and replay.
  • 🔄 The loop integrates with Heart runtime events and BrainProvider calls to make pacing and retries explicit and testable (many unit tests added).
  • 🪪 Snapshots and deterministic decisions make debugging and e2e validation (tests/e2e/test_server.py) reproducible.

📚 DX, docs & reliability

  • 📝 Docs and blueprints updated (README.md, docs/architecture/blueprint.md, AGENTS.md) to document the runtime contract and configuration.
  • 🧰 CI-friendly changes and a fuller pyproject + lock (pyproject.toml, uv.lock) — improved reproducibility.
  • 🧩 API and error surface tightened (src/ansina/api/* and src/ansina/errors.py) so telemetry and retries are actionable.
  • 🔍 Numerous unit tests added/extended across API, brain, heart, and logging to keep future refactors safe.

This merge moves Ansina from prototype toward a composable runtime: clear contracts for heartbeats, a pluggable brain interface, and an autonomic loop that keeps the agent moving even when things fail.

Onward — building agents with both heart and brain.

Ansina Rock‑Solid Skeleton: packaging, config, logging, REST API, auth, persistence, CI, docs

August 21, 2026
ai-agentai-mcpai-engineerView source PR ↗

Laid the foundation: core infra, API, auth, persistence, CI, tests and docs — a production-minded M0 skeleton for Ansina.

Lead A focused engineering sweep to make Ansina reviewable and operable without any model code. The PR delivers a full infra baseline (packaging, config, logging already merged via #19), then adds REST endpoints, auth, persistence primitives, CI gates, testing conventions and docs scaffolding. All checks (unit + e2e, both OS legs) are green; branch is still a draft pending final author flip to “ready for review.”

🚧 What this PR adds

  • 🧩 REST: 🛣️ API skeleton and middleware (src/ansina/api/app.py, routes/health.py, middleware.py, exception_handlers.py) to standardize request/response flows.
  • 🔐 Auth: 🪪 API authentication and public endpoints (src/ansina/api/auth.py) with tests to validate behavior.
  • 💾 Persistence: 🧰 DB foundation, migrator and initial migration (src/ansina/storage/database.py, migrator.py, migrations/0001_init.sql).
  • 📄 Docs & README: 📝 README rewrite and docs scaffolding (docs/architecture/blueprint.md) to explain architecture sections §3–§5.

⚙️ CI, tests, and quality gates

  • 🧪 Tests: ✅ Unit + E2E tests added (tests/unit/*, tests/e2e/test_server.py) and a testing strategy that defines unit conventions + E2E build validation gate.
  • 🔁 CI pipeline: 🛠️ .github/workflows/ci.yml implements unit and E2E jobs; both OS legs pass on CI.
  • 🧹 Hygiene: ✨ pre-commit, Makefile, pyproject tweaks and .gitignore updates to keep developer UX smooth.

🧭 Engineering choices & tradeoffs

  • ⚖️ Separation: Model code intentionally excluded — M0 focuses on stability, contracts, and reproducible ops before wiring inference.
  • 🧪 Validation-first: E2E gate ensures the runnable artifact builds cleanly on multiple OS runners before merging downstream model work.
  • 📚 Documented: Architecture blueprint and examples (ansina.example.toml) make onboarding and future design decisions explicit.

🔎 Files and signals worth scanning

  • 🔁 CI + tests: .github/workflows/ci.yml, tests/e2e/test_server.py
  • 🧭 API surface: src/ansina/api/app.py, auth.py, routes/health.py, readiness.py
  • 🗄️ Storage: src/ansina/storage/database.py, migrator.py, migrations/0001_init.sql
  • 📘 Docs: README.md, docs/architecture/blueprint.md

This was a foundation-first sprint: stable contracts, testable CI, and readable docs — readying Ansina for the next phase where model integration becomes a consumer of this platform.

Onward to M1 — wire the models once the infra is battle-tested.

M0 skeleton: packaging & toolchain, layered configuration, and structured logging

August 19, 2026
ai-agentai-mcpai-engineerView source PR ↗

Ship the foundations so higher-level intelligence has a stable home.

Lead: Merged a stacked change set that establishes the M0 skeleton for ansina — packaging, layered configuration, and structured logging with an error taxonomy. This is infrastructure-first: small, test-covered primitives that make future agent work reliable and observable.

🚀 Packaging & Toolchain

  • 🧰 Added reproducible packaging via pyproject.toml and uv.lock to pin deps for predictable builds.
  • 🛠️ Makefile provides simple developer ergonomics (build, lint, test) so contributors can focus on features.
  • 📦 Project layout: src/ansina, py.typed, and sensible .gitignore / .pre-commit-config.yaml to catch regressions early.
  • 🔁 CI-ready baseline: commits and scaffolding aim to make downstream PRs (like config & logging) trivial to merge.

🧭 Layered Configuration System

  • 🗂️ Introduced a layered settings model (src/ansina/config/settings.py) that merges defaults, env, and ansina.example.toml.
  • 🔁 Enables runtime overrides and environment-specific profiles while keeping a clear canonical source-of-truth.
  • 🧪 Tests added (tests/unit/config/test_settings.py) to validate precedence, parsing, and edge-cases — configuration is code, and now it’s tested.
  • 📄 Documented intent in docs/architecture/blueprint.md (sections 3–5) so config decisions are traceable.

🔍 Structured Logging & Error Taxonomy

  • 🧾 Structured logging modules (src/ansina/logging/*) introduce context, formatter, redaction, and setup helpers for consistent logs.
  • 🧩 Error taxonomy (src/ansina/errors.py) classifies failures so observability and retries can be systematic instead of ad-hoc.
  • 🔒 Redaction utilities keep sensitive fields out of telemetry; formatters standardize JSON-friendly output for downstream ingestion.
  • ✅ Coverage: unit tests (tests/unit/logging/* and tests/unit/test_errors.py) ensure shape, context propagation, and redaction behave as expected.

🛠️ Why this order mattered

  • 🧱 Packaging first: a stable toolchain made subsequent config and logging work reproducible across machines and CI.
  • 🔗 Layered config next: it lets apps behave correctly in dev, CI, and production without code changes.
  • 📡 Logging & errors last: with predictable packaging and config, logs map to real runtime semantics and tests can assert behavior.

What’s parked or next

  • ▶️ #3 (structured logging) was merged together for cohesion; CI green gates remain a priority before broader feature work.
  • 🔭 Next: wire observability into agent runtime paths and start collecting real traces during e2e experiments.

Small wins like these are quiet but multiply: fewer surprises, faster iteration, and safer experiments.

Building Ansina: A Blueprint for a Deterministic, Dual-Model AI Runtime

August 16, 2026

Building Ansina: A Blueprint for a Deterministic, Dual-Model AI Runtime

Designing a lean, provable AI agent runtime built from the ground up on Python ≥ 3.14: introducing the architecture blueprint for Ansina.

Currently in its blueprint phase and moving rapidly toward initial implementation, Ansina focuses on a tight, deterministic core where every architectural choice serves operational control, high signal-to-noise ratio, and zero unnecessary bloat.

Here is an inside look at what Ansina brings to the table as it prepares to launch:

📐 Pure Hexagonal Architecture

  • Streamlined REST Surface: Exposes a clean, single internal FastAPI REST API with zero channel bloat or unnecessary gateway protocol overhead.
  • Deliberate State Management: Starts with a minimal SQLite schema, growing persistence intentionally rather than accumulating hundreds of unmanaged state tables.
  • Never-Throw Streaming Contracts: Features an ApiProvider port where streaming errors are returned synchronously as terminal stream events, guaranteeing predictable error paths.
  • First-Class Redaction: Implements structured logging with redaction hardcoded into the formatting pipeline to ensure sensitive data is filtered before it hits disk.

🫀 The Dual-Model Core: Heart & Brain

Ansina separates autonomic local liveness from remote cognitive reasoning to eliminate round-trip network hops for basic decisions:

  • The Heart (In-Process Autonomic Loop):

  • Uses an embedded ≤4B parameter model running natively in-process via MLX on Apple Silicon (with a llama-cpp-python fallback).

  • Bounded strictly by an 8k context window to ensure optimal prompt performance.

  • Drives an always-on tick loop tasked exclusively with deciding idle vs. act vs. escalate without network overhead.

  • The Brain (Remote High-Reasoning):

  • Connects to 35B+ parameter models via an OpenAI-compatible BrainProvider port for complex reasoning tasks.


🎯 Hardware Target & Testing Strategy

  • Hardware First: Optimized explicitly to run as an always-on engine on local Apple Silicon hardware (M4 Mac Mini, 16GB unified memory).
  • Black-Box Verification: Uses an E2E test harness that spins up python -m ansina as an isolated subprocess, testing readiness, migrations, and auth strictly over HTTP.

The design is locked, the constraints are set, and implementation of M0 — Skeleton is about to begin.

Single-agent A2A pivot — park the tri-node, preserve the work

August 12, 2026
ai-agentai-a2aai-mcpView source PR ↗

Hook: We pivoted to a pragmatic architecture — single-agent A2A — while keeping the original tri-node design one uncomment away.

Lead: After end-to-end tests showed sub-agent → A2A is unreliable in OpenClaw, the team chose stability and observability over brittle enforcement. The result: main does cross-agent calls directly, tri-node is parked (preserved), MCP tooling added, and docs + tests updated.

🔁 Why we pivoted

  • ⚠️ Sub-agent A2A break: tests showed a sub-agent cannot reliably drive another agent over A2A; the break is the sub-agent+cross-agent combination.
  • 🔍 Practical trade: preserve the tri-node design, but avoid production fragility by routing A2A from main→main.
  • ♻️ Preserve, not delete: tri-node config, prompts, and workspaces remain in-place as JSON5 comments and IDENTITY.tri-node.md (tag v0.1.0 = revival baseline).

🛠️ What changed (concrete)

  • 🧭 Single-agent mode: both containers make main the working agent; when code is needed Researcher main curls Coder at http://coder:3000/a2a/tasks.
  • 🗂️ JSON5 preservation: sub-agent entries commented in openclaw.json so revival is one uncomment away.
  • 🧰 Local MCPs: added filesystem MCP for Coder and memory MCP for Researcher; tools surface as bundle-mcp:<server>__<tool>.
  • 🧪 Orchestration + tests: index.js remained logic-identical (comments updated); tmux vm-bridge used for live runs and probes.

What we validated

  • 🔎 Spawn logs: live single-agent tasks showed spawn_count = 0 — no sub-agent spawn.
  • ↔️ A2A round-trip: Researcher→Coder→Researcher worked in live tests (Coder logged payload, returned structured JSON, Researcher folded result.output).
  • 🧰 MCP probe: openclaw mcp doctor files → files: ok; doctor memory → memory: ok; live tool calls returned expected results.
  • ⚙️ Docs & artifacts: AGENTS.md + README updated, diffs and diffstat adjusted, IDENTITY.tri-node.md preserved, tag v0.1.0 recorded.

📌 Edge cases, constraints & next steps

  • 🪪 Prompt hygiene: observed headless-output violations (narration prefixes, markdown fences) and a fallback self-write when Coder was down — these are prompt-enforcement issues to fix, not A2A plumbing failures.
  • 🔁 Operational note: to pick up MCP config changes, restart the container (sudo podman restart) — do NOT use pm2 restart openclaw-gateway (it can orphan the gateway).
  • 🛠️ Revival path: once OpenClaw fixes cross-agent delegation from sub-agents, simply uncomment JSON5 entries + swap IDENTITY.tri-node.md back to resume tri-node operation.
  • 🚦 Next: tighten prompt enforcement, add CI smoke-tests for A2A round-trip, and monitor upstream OpenClaw fixes to evaluate revival.

Final: Practical pivots win — preserved the long-term design while shipping a robust, testable PoC for cross-agent work.