Skip to main content
Version: Next

ivoai architecture

Status: implementation baseline for v0.5.0. Decisions and upstream data were validated through 2026-08-25. Exact pins live in manifest/components.yaml; this document explains why they exist and how the pieces fit together.

Goals and boundaries

ivoai is one Go binary with client and server modes. A client is useful immediately after setup, with no external connection and no pay-as-you-go API key. ChatGPT, Claude, and an ivoai server are deliberate post-setup connections, all managed through CLI commands.

Optional memory, context, compression, and orchestration setup failures are isolated from local agent launch. A failed compression preflight selects the direct agent. After a wrapper process starts, its exit status is propagated rather than risking a duplicate interactive session.

The core is organization-neutral. It contains no company domains, private addresses, user paths, or preconfigured connectors. Git is optional; project mode is an explicit override, not a prerequisite.

Multi-server knowledge plane

The client-side knowledge path is provider-neutral and does not make every enrolled server globally visible to every agent:

managed session (Codex / Claude / AUTO workers)
│ local capability, selected sources

per-session loopback KnowledgeRouter

├── ServerPool purpose A ── primary/standby redundancy
└── ServerPool purpose B ── independent Memory/Context

ServerProfile records an opaque ID, alias, purpose, optional redundancy group, priority, protocol, endpoints and features. Credentials are stored separately by opaque ID. The pool deterministically groups equivalent replicas while preserving purpose boundaries. No company name appears in core routing logic.

The router binds an ephemeral 127.0.0.1 listener for each session. It issues a random short-lived local capability, retains upstream credentials in process, rejects cross-origin redirects and forwards each credential only to its matching profile. Codex receives process-local MCP arguments and Claude a private runtime MCP file. AUTO uses the same router through failover; workers inherit local endpoints. No global mutable MCP rewrite is needed, so sessions for different purposes can run concurrently.

Single-source authoritative responses are preserved. Unfiltered sessions federate all enabled sources; explicit selectors restrict that set. Federated tools/call reads fan out concurrently with per-source deadlines and deterministically merge source-attributed results. Requests are capped at 4 MiB and upstream responses at 16 MiB. Writes require exactly one logical destination; a redundancy write is primary-only and is not retried after an uncertain side effect. Read failover is limited to the same purpose/redundancy group and uses bounded circuit state.

This router is not MemoryBackend, ContextBackend, WorkingContext or replication. ai-memory remains durable operational memory at the selected server; Context remains read-only private knowledge; ArtifactStore remains exact transient evidence. See Multi-server knowledge sources.

Replaceable core contracts

The runtime boundaries in internal/core are deliberately small and are not part of the persisted config, state, ownership, component, or server schemas. They provide provider-neutral identities, capability support (supported, unsupported, unknown, or not_exposed), health, compatibility, provenance, lifecycle and explicit fallback metadata. Installed, available and healthy are separate facts; missing probe evidence remains unknown rather than being inferred from a provider or model name.

The current adapters are:

  • CodexExecutor, ClaudeExecutor, and direct-only OpenCodeExecutor over the existing official interactive CLI runtime. They expose session start and bounded cancellation; incremental send, structured primary results and diff retrieval are not claimed.
  • AIMemoryBackend over the existing ai-memory client configurator. Memory content calls remain behind the authenticated MCP boundary rather than expanding this lifecycle interface.
  • LegacyQdrantContextBackend over the existing catalog, local embedding and Qdrant context service. The agent-facing contract stays read-only; ingestion remains an administrative composition.
  • HeadroomCompressionProvider over the existing wrapper probe and invocation. The IVOAI fidelity policy still decides when exact Memory/Context results require a bypass, and the official client remains the direct fallback.
  • CompressionProvider: Caveman, Headroom e bypass direto records the Caveman default and migration boundary. Providers are mutually exclusive; Direct remains the safety fallback and explicit Headroom remains a temporary compatibility path.
  • Caveman canary and fidelity evaluation defines the deterministic corpus, byte-exact gates, opt-in pinned-asset smokes and the evidence used to approve the Caveman default while retaining Direct fallback.
  • RufloOrchestratorAdapter over the existing safe-profile control plane. It exposes only ephemeral swarm and opaque lifecycle coordination; scheduling, routing, inference, quota and durable memory remain owned by IVOAI.

SkillSource, SkillRegistry and ToolProvider currently define only base identity, probe and discovery contracts. No new skill pack or tool provider is loaded by this foundation. Doctor builds a runtime-only component matrix from existing state and official probes so future selection and fallback can explain which implementation is active and why. The matrix is never written to session/config persistence and does not require a migration.

System view

CLIENT SERVER

ivoai CLI/wizard one public HTTPS origin
| |
+-- configuration + ownership +-- /.well-known/ivoai
+-- connection registry +-- /health and /ready
+-- official agent authentication +-- enrollment/control API
+-- fail-safe memory hooks +-- context MCP (read-only)
+-- session control plane +-- ai-memory MCP
| +-- direct observability
| +-- AUTO bootstrap + DAG scheduler
| `-- WorkingContext + private ArtifactStore
| |
+-- Headroom -- Codex/Claude +-- Web MCP + OAuth 2.1
`------ direct fallback +-- context service
| +-- connectors/ingestion
| +-- local embeddings
| `-- Qdrant (internal)
`-- ai-memory (internal)

Automatic quota-aware control plane

ivoai auto adds a supervisor and deterministic scheduler above the existing session control plane. The selected official Codex or Claude TUI remains planner, primary, conversation owner, and result consolidator. Its first substantive turn attempts Memory then Context exactly once, creates a private bounded SharedContextBrief, validates a maximum-12-task DAG, and submits numerical task signals to IvoAI. IvoAI—not the model—calculates capability scores, chooses the lowest sufficient runtime-verified model/effort profile, rejects uneconomic delegation, and runs dependency-ready advisory workers asynchronously. A standalone quota manager gates the initial primary and every worker; Ruflo remains a provider-free ephemeral lifecycle coordinator. Codex quota comes from the official app-server JSON-RPC method, while Claude quota comes from a session-local structured statusline payload. Context-window pressure is modeled separately from subscription quota.

The supervisor owns process identity and bounded failover. It persists only session metadata, normalized quota snapshots, and secret-free checkpoints. On a confirmed hard limit it reaps the primary process group, re-probes the alternate, reads a bounded worktree summary without changing files, and opens the alternate official TUI with the handoff. A two-failover ceiling prevents ping-pong loops. See Automatic orchestration and Subscription quota routing. Scoring, capability discovery, and the async API are specified in Automatic scheduler.

Only the gateway is externally reachable. Qdrant, the embedding runtime, ai-memory administration, and service management stay on a private Compose network or loopback socket. The server does not expose a shell or an arbitrary-command endpoint.

Client architecture

The public installer downloads a release archive and verifies its published checksum. When invoked from an authenticated source checkout, it first uses a compatible system Go toolchain. If Go is absent or older than the go.mod directive, it downloads the reviewed linux/amd64 or linux/arm64 Go archive, verifies the pinned official SHA-256, and builds with temporary module and build caches. The toolchain and caches are removed when installation finishes; no global Go installation or package-manager mutation is required.

Files and ownership

The client follows the XDG Base Directory Specification:

  • configuration: $XDG_CONFIG_HOME/ivoai or ~/.config/ivoai, mode 0700;
  • persistent application data: $XDG_DATA_HOME/ivoai or ~/.local/share/ivoai;
  • operational state and ownership manifest: $XDG_STATE_HOME/ivoai or ~/.local/state/ivoai;
  • cache/downloads: $XDG_CACHE_HOME/ivoai or ~/.cache/ivoai;
  • secrets: $XDG_CONFIG_HOME/ivoai/secrets.json (or ~/.config/ivoai/secrets.json), mode 0600, inside the mode-0700 config directory.

The main TOML contains connection status and non-secret settings only. The ownership manifest records component executables as managed or pre_existing; all ivoai-managed binaries, hooks, and component runtimes live below the ivoai XDG data root. Uninstall removes the ivoai XDG roots and installer registration while preserving pre-existing third-party executables and vendor authentication. Integrations use each third-party client's supported configuration mechanism and preserve unrelated settings.

The install catalog compiled into the binary mirrors the reviewed central manifest and selects exact OS/architecture assets. Direct archives are verified against reviewed SHA-256 values before bounded extraction. Headroom ships architecture-specific hashed constraint locks and permits wheels only. Ruflo ships a complete npm v3 lock and installs with npm ci and lifecycle scripts disabled; every registry dependency has an integrity value. Both installers run with a minimal environment that excludes user and provider credentials.

Managed launchers use atomic replacement. Downloads have size limits and timeouts. latest is never a component install target. Updates are explicit through ivoai update and use a versioned compatibility probe, private exact-file snapshots, an ordered reversible migration registry, atomic promotion, and post-update Doctor. Rollback restores both the previous executable and its compatible IVOAI-owned persistence. Unknown TOML fields are retained through a raw-document plus typed-projection merge. The complete invariant is documented in production-compatibility.md.

Managed Codex includes the same-version codex-code-mode-host published as a separate official release asset. Both architecture-specific archives are independently checksum pinned. The host has no version command, so its compatibility identity is the reviewed release version recorded in managed state; a missing or mismatched companion fails setup/launch instead of silently removing the entire tool surface.

Connections and official credentials

ivoai connect chatgpt delegates authentication to codex login; codex login status is the supported status probe. OpenAI's official documentation states that ChatGPT subscription login is supported and opens a browser. ivoai never reads or stores the returned credential. Source: ChatGPT authentication.

ivoai connect claude checks claude auth status, starts the official claude auth login browser flow when needed, and validates status again. Anthropic documents Pro, Max, Team, and Enterprise subscription access without an API key. Source: Claude Code authentication.

The exact managed Claude pin is 2.1.228 because Anthropic's stable registry channel pointed there on the validation date while latest was 2.1.237. Anthropic describes stable as delayed and regression-filtered. ivoai installs this reviewed asset and changes its managed pin only during an explicit setup or update. Behavior implemented inside the upstream Claude executable remains governed by Anthropic.

ivoai does not inspect ~/.codex/auth.json, Claude cookies, or OAuth tokens. It does not ask for, proxy, copy, or log credentials. A pre-existing official install and login remain owned by the user.

Agent launch and failure isolation

Research-source selection is centralized in internal/knowledgepolicy. Interactive primaries, automatic sessions, and background workers receive the same process-local contract: memory first, Context second, and external web sources only afterward when internal knowledge is unavailable, insufficient, stale, or independent verification is requested. Local working-tree inspection and tasks completely specified by the user do not create artificial research calls. This is an instruction-layer invariant; the official clients continue to own their tools and authentication.

The launcher uses structured argv and signal forwarding. Interactive clients inherit ivoai's foreground process group; creating a new group without tcsetpgrp would suspend terminal reads with SIGTTIN. Terminal and shell job-control interrupt, suspend, continue, and resize signals therefore reach the complete interactive stack, while context cancellation uses a bounded TERM-to-KILL sequence. The normal decision tree is:

requested agent
-> ivoai-memory or ivoai-context MCP active?
yes -> official agent [argv...]
no -> requested CompressionProvider healthy and compatible?
caveman -> managed Caveman proxy -> official agent
headroom -> headroom wrap <agent> [argv...]
direct or failed preflight -> official agent [argv...]

Headroom 0.36.0 is deprecated but retained during the Caveman observation window. It officially supplies wrap codex and wrap claude and is installed into an isolated tool environment using uv 0.12.5 and the exact managed CPython 3.13.15 runtime below the ivoai data root. An unavailable, unhealthy, or incompatible preflight selects the direct agent. After a compatible wrapper process has started, ivoai propagates its exit status; it does not silently retry a failed interactive session. ivoai does not rewrite the user's aliases or replace third-party launchers. Headroom 0.36.0 can apply lossy compression to Codex Code Mode custom_tool_call_output items without reliably associating them with the originating MCP tool name. IvoAI therefore bypasses any lossy compression provider whenever authoritative shared knowledge is active for the session. This provider-neutral policy preserves exact Memory and Context results; session telemetry records the requested provider, effective Direct path and bounded reason. Sources: Headroom CLI, Headroom v0.36.0, Headroom issue 940, uv 0.12.5, and Python 3.13.15.

Session control plane

Session metadata is an explicit domain below the XDG state directory. Random IDs, atomic private files and Linux PID start markers support lifecycle monitoring without creating another conversation store. Prompts, results, tokens and raw environments are excluded. Direct sessions call the same interactive runtime as ivoai codex and ivoai claude; Ruflo is not touched.

Concurrent primaries do not share lifecycle state: each session has its own record, process identity and runtime subtree, and every Ruflo command runs with that subtree as both working directory and isolated HOME. Session and quota stores serialize cross-process mutations with advisory locks and publish complete files by atomic replacement. Shared ai-memory pages are the deliberate exception: they form the common durable knowledge plane used by all official clients.

Orchestrated sessions have a strict gate: the safe profile must match its reviewed tool allowlist, provider execution and durable Ruflo memory must be false, Ruflo must pass a version/health command, a real hierarchical swarm must return a verifiable ID, and an opaque primary task must be registered before the official client starts. Ruflo gets a clean environment with RUFLO_PROVIDER=ivoai-disabled and CLAUDE_FLOW_MEMORY_BACKEND=memory.

The local stdio ivoai-orchestrator MCP is injected only for the lifetime of that primary. It maps delegation to trusted official Codex/Claude executables and maps opaque lifecycle IDs to Ruflo task commands. Exact worker evidence is written first to the private transient WorkingContext ArtifactStore. The bridge gives the primary only a bounded provider-neutral WorkerResult (summary, findings, advisory StateDelta, and opaque ResultRefs). Session state stores only those bounded references, never worker bodies. Explicit local read-only tools recover an exact artifact or bounded range after ownership, TTL, size, and digest validation. Worker concurrency defaults to two and is hard-capped at three. The bridge is not registered in or routed by the public server gateway. See orchestration.md and WorkingContext.

Automatic plans add metadata-only task IDs, dependencies, scores, tiers, profile sources, execution state, duration, and escalation reasons to session state. Full task instructions, SharedContextBrief content, worker responses, credentials, and environments are never sent to Ruflo. Exact responses remain private in the transient ArtifactStore; session metadata and failover handoffs contain only ResultRefs. Codex workers are sandboxed read-only and disable inherited MCPs except managed Memory/Context read tools. Claude workers use strict process-scoped MCP configuration and plan permission mode with mutation tools disabled. The primary alone applies changes.

ai-memory 1.29.0 is the durable operational memory layer. Its versioned native binaries and hook bundle support Codex and Claude Code and publish per-platform checksums. Hooks enqueue with bounded latency and treat network, authentication, and service failures as non-fatal; hook failure results in the allow/zero-exit path appropriate to the host. Connecting a server rewrites only ivoai-owned memory endpoint metadata. The authoritative upstream release is ai-memory v1.29.0; its changelog was reviewed because this is newer than the 1.28.1 floor in the product requirements. IvoAI installs lifecycle hooks with ai-memory's repo-root project strategy. Project resolution therefore uses the main Git repository name across Linux/WSL path aliases, subdirectories and linked worktrees instead of splitting observations by the client process's spelling of the current directory. Explicit MCP queries still include the global scope according to ai-memory's normal query semantics.

Ruflo 3.38.12 is installed for workflows, coordination, and skills. ivoai registers a least-privilege MCP profile containing only coordination tools, process-local temporary memory, and a wrapper that strips supported PAYG-provider credential variables before Ruflo starts. Provider execution and durable Ruflo memory remain disabled; Codex and Claude themselves continue to run through their official subscription-authenticated clients. Upstream issues #2356 and #2962 remained open and showed direct execution selecting separately configured providers, so those paths are deliberately excluded from the default profile.

Identity and MCP registry

The connection registry models MCP entries uniformly by stable ID, transport, URL/argv, scopes, ownership, and enabled status. Context and memory use the same registry as user-added MCPs; agent-specific renderers are edge adapters, not separate sources of truth.

The default ivoai client identity outside an explicitly initialized project is host:<normalized-hostname>. It is independent of the current directory, so /etc, /opt, and /var/lib do not become accidental projects. ivoai project init creates a deterministic ID from the absolute Git root and a local .ivoai.toml marker that overrides host identity. Merely entering a Git repository does not silently change ivoai identity. ai-memory lifecycle scoping is a separate concern and uses the stable main-repository strategy described above.

Server architecture

Runtime and persistence

Supported initial hosts are Ubuntu 22.04+, Ubuntu 24.04+, and Debian 12 on amd64 or arm64. The ivoai gateway and context processes are systemd services running as distinct non-login ivoai-gateway and ivoai-context users in the shared ivoai group. Units use Restart=on-failure, NoNewPrivileges=yes, a restrictive UMask, capability removal, hidden process views, private temporary directories, and write allowlists limited to their own data paths. Backend containers use the separate non-login ivoai identity.

Layout:

/etc/ivoai/ non-secret server configuration
/etc/ivoai/secrets/ 0700 directory, 0600 secret files and managed TLS copies
/var/lib/ivoai/context/ metadata, normalized corpus and ingestion state
/var/lib/ivoai/memory/ ai-memory authoritative data
/var/lib/ivoai/qdrant/ rebuildable vector index
/var/lib/ivoai/qdrant-snapshots/ Qdrant snapshot workspace
/var/lib/ivoai/qdrant-init/ Qdrant non-secret initialization marker
/var/lib/ivoai/models/ pinned embedding model snapshot
/var/lib/ivoai/enrollment/ hashed enrollment/client credential records
/var/lib/ivoai/backups/ versioned backup archives
/run/ivoai/ sockets/PIDs and other ephemeral state

Service stdout and stderr go to journald by default. CLI-rendered server logs and diagnostics pass through ivoai's redactor for labeled authorization, enrollment-code, and common API-key forms. Authentication handlers do not log request bodies or credential values; operators must still review journald output before sharing it.

Docker Compose is reserved for Qdrant, Text Embeddings Inference, and ai-memory. Every image is referenced by immutable digest, is attached to an internal network, and has no host port unless a loopback-only compatibility port is unavoidable. ivoai manages the Compose project and volumes idempotently. ivoai's own Go services remain ordinary systemd executables and require no private ivoai image. Setup requires Docker Engine 28.0.0 or newer because deterministic gateway priority is part of the backend egress boundary; it validates the daemon before writing server assets and does not replace the operator's Engine installation. Where compatible Compose is not packaged, ivoai installs the reviewed Docker Compose 5.5.0 CLI plugin for amd64 or arm64 from its official release, verifies the manifest SHA-256, and never selects a floating latest artifact. Direct-TLS certificate and key copies are service-owned 0600 files under /etc/ivoai/secrets/tls. After systemd loads its Qdrant and embedding environment files, the context service's sandbox makes the complete secrets tree, enrollment state, and memory state inaccessible. The gateway has a separate denial list for memory, Qdrant, model, and corpus filesystem data outside its control-plane responsibilities.

Protocol version 1

The public discovery response is non-sensitive. The gateway applies Cache-Control: no-store consistently to discovery and API responses:

{
"protocol_version": 1,
"server_version": "0.1.0",
"public_base_url": "https://ai.example.com",
"health_endpoint": "/health",
"ready_endpoint": "/ready",
"enrollment_endpoint": "/v1/enroll",
"context_mcp_endpoint": "/v1/mcp/context",
"memory_mcp_endpoint": "/v1/memory/mcp",
"memory_hooks_endpoint": "/v1/memory",
"web_mcp_endpoint": "/mcp",
"oauth_authorization_server_metadata": "/.well-known/oauth-authorization-server",
"features": {"context": true, "memory": true, "memory_hooks": true, "web_mcp": true, "oauth_pkce": true, "remote_admin_read_only": true}
}

The gateway exposes this response at GET /.well-known/ivoai. /health is process liveness and does not depend on optional connectors. /ready requires a healthy context service; an unavailable ai-memory dependency is reported as ready_degraded so context remains usable.

Before consuming an enrollment code, the client requires valid Web PKI TLS, a healthy or degraded-ready server, and protocol major 1. After enrollment, the one-time credential and MCP registry are persisted before context and memory probes. Probe failures become explicit degradation warnings, so a consumed code never strands the issued credential. Redirects to another origin are rejected during discovery and enrollment. Plaintext is accepted only on loopback for development or behind a same-host TLS-terminating reverse proxy. A remote reverse proxy requires an explicit source CIDR and forwarded HTTPS enforcement; other non-loopback listeners require direct TLS configuration.

Endpoints use bounded JSON bodies and server-level request, read, write, and idle timeouts. MCP endpoints accept JSON-RPC over HTTP and require bearer authentication. Protocol changes that preserve version 1 are additive; removing or changing a field or tool requires a new protocol major. Database and Qdrant collection migrations carry their own monotonically increasing schema versions.

Enrollment and authorization

ivoai server enrollment create generates 256 secret bits from the operating-system CSPRNG and displays the base64url one-time code once. Because these high-entropy random codes resist offline guessing, the server stores only a SHA-256 hash, together with the ID, creation time, expiry, scopes, and consumed/revoked timestamps. The default TTL is 10 minutes.

Codes are compared in constant time. Mutations take an exclusive OS file lock, and a successful exchange atomically replaces the state file after marking the code as consumed. This prevents independent CLI and gateway processes from consuming or overwriting the same state concurrently. Codes and returned credentials are never placed in argv examples, URLs, or logs; standard input and an interactive no-echo prompt are supported.

The exchange returns a random client-scoped bearer credential once. Only its hash and metadata are stored server-side in the owner-only local enrollment backend at /var/lib/ivoai/enrollment/state.json; the client stores the value in a 0600 secret file. Initial scopes are context:read, memory:read, memory:write, status:read, doctor:read, and connector:read. Administrative mutation is never implied by enrollment. Revocation is immediate. Invalid, expired, consumed, and revoked codes share one uniform external error, so code state is not disclosed.

Web MCP and OAuth

ChatGPT Web and Claude Web use a separate public integration boundary. /mcp is a Streamable HTTP MCP endpoint built on the pinned official MCP Go SDK. It aggregates context and memory behind one connector URL while preserving the existing native client MCP routes. initialize advertises server instructions, tool capabilities, and the bounded skills extension; tools return typed structuredContent plus a text representation for compatibility.

The Web tool surface is intentionally narrower than the backing services:

  • context_search, context_get_document, context_recent, and context_health are read-only and label retrieved documents as untrusted data;
  • memory_query, memory_recent, memory_read_page, and memory_status require memory:read;
  • memory_write_page and memory_feedback require memory:write;
  • memory_delete_page requires memory:delete, a normalized page path, and explicit confirmation in the call.

Maintenance, self-improvement, handoff orchestration, arbitrary upstream tools, and host commands are not published by this facade. Loss of ai-memory degrades memory tools without making context tools or gateway liveness unavailable.

OAuth follows Authorization Code with PKCE S256. Protected-resource and authorization server metadata support Web connector discovery; dynamic client registration accepts only validated HTTPS redirect URIs (plus the explicitly supported loopback development case). Authorization codes live for five minutes, access tokens for one hour, and rotating refresh tokens for 30 days. The browser consent flow additionally requires a short-lived one-time activation code created by the local administrator, avoiding a new password database.

Server-side OAuth persistence stores only hashes of activation codes, authorization codes, access tokens, and refresh tokens. Mutations are locked and atomic. Scopes are checked again at the tool boundary. Origin validation, PKCE verification, state round-tripping, exact redirect matching, token rotation, and revocation limit token substitution and cross-site authorization attacks.

The RFC 8707 resource value is the canonical public /mcp URL. It is preserved in authorization codes, access tokens, rotating refresh tokens, and every authenticated MCP request so credentials cannot be replayed against a different audience.

MCP skill delivery

The repository's skills/ivoai-memory-context/SKILL.md is also available through skills/list, skills/get, and resources/read. The advertised resource uses a skill:// URI and digest so compatible clients can import a fixed snapshot. A release also publishes the same directory as ivoai-memory-context.zip for Claude custom Skill import. MCP initialization instructions, read-tool descriptions, and the skill all declare the same memory → Context → web order. Importing instructions cannot technically guarantee that a Web model invokes a tool on every turn; the platform retains final tool-selection control.

Remote administration is an explicit allowlist of typed operations such as status, doctor summary, and connector list. There is no host-command parameter, executable-path parameter, shell, file-write primitive, or generic proxy endpoint.

Context and RAG

The context core is healthy with zero connectors and zero documents. A connector implements discovery and streaming reads into a normalized document interface; it cannot write the server filesystem outside its configured source. v0.1 adapters are:

  • filesystem: a configured local root; directory traversal skips unsafe names and symlink entries;
  • git: an existing local checkout enumerated with bounded git ls-files and then filtered by the same document rules.

Future Drive, S3, Notion, HTTP, GitHub, and generic MCP adapters implement the same interface without changing chunking, storage, or agent tools. Connector credentials are separate scoped secrets.

Pipeline:

connector -> canonical document -> sensitive/binary filter -> deterministic chunks
-> local embedding -> versioned Qdrant collection -> read-only MCP tools

Normalization records a stable document ID, source, relative path, timestamps, and connector metadata. The filter excludes secret, credential, cloud-state, and key files; binary content; non-regular files; hidden VCS internals; and files above 8 MiB. Git enumeration disables repository-controlled hooks and fsmonitor programs.

Ingestion opens the connector root and every path component with OS-level no-follow semantics, rejects traversal and special files, reads through the verified descriptor, and enforces aggregate document, byte, and chunk quotas. Catalog replacement is batched. Re-ingestion reconciles disappeared documents, while connector removal purges that source's catalog entries and Qdrant vectors before deleting its registry entry.

Chunk IDs derive from document ID, chunk index, and chunk text. The default policy uses up to 1,200 Unicode code points with approximately 150-code-point overlap and prefers newline or word boundaries. Re-ingestion is deterministic, but Qdrant currently deletes a document's old points before uploading its replacement. A failed upload therefore requires another ingestion run.

The Qdrant collection is ivoai-context-v1-d384; Qdrant 1.19.0 uses the pinned upstream unprivileged multi-architecture image digest. Its host mapping is loopback-only (127.0.0.1:6333) and requires a generated API key. Embeddings and ai-memory use separate generated credentials. Docker uses a transient publication network to establish the loopback bindings and a separate model-download network with explicit highest gateway priority. After the embedding model is healthy, systemd disconnects the download route. The loopback-publication network remains attached so the host mappings stay valid, but disables IP masquerading and therefore provides no backend egress. The index is a cache; source documents, normalized metadata, and connector definitions are authoritative. A collection can be deleted and rebuilt deterministically.

Agent tools are context_search, context_get_document, context_recent, and context_health. They are read-only and return source metadata. Search and recent counts are bounded to 100; context_recent omits document bodies; and context_get_document returns at most one document, subject to the 8 MiB ingestion limit. Ingestion and connector administration use separate authenticated commands and API routes.

Document text is untrusted data. Tool descriptions and results label it as such and never route document-provided commands into installers, shells, connector configuration, or authorization decisions.

Local embedding decision

Text Embeddings Inference (TEI) 1.9.3 was chosen over a cloud API because it is Apache-2.0, maintained by Hugging Face, exposes a small HTTP API, supports CPU containers, and documents both x86_64 and aarch64. The amd64 image and the upstream cpu-arm64-sha-30507cb arm64 image are each pinned by immutable OCI index digest; neither architecture uses a mutable tag at runtime. Sources: TEI v1.9.3 and TEI.

The default model is intfloat/multilingual-e5-small at immutable revision 614241f622f53c4eeff9890bdc4f31cfecc418b3, with its safetensors hash recorded in the manifest. It is MIT-licensed, 384-dimensional, supports 94 languages, has a 512-token limit, and is suitable for Portuguese/English CPU-first retrieval at moderate footprint. E5's required query: and passage: prefixes are applied centrally, and the Qdrant collection name includes the dimensional/schema version. Model source: multilingual-e5-small pinned revision.

TEI was preferred to embedding directly inside the Go gateway because process isolation bounds native-model crashes and permits independent health, restart, and resource limits. FastEmbed 0.8.0 is a credible lower-footprint future alternative, but it would add a Python runtime inside the context process or another bespoke service without improving the current HTTP boundary.

Backup and restore

A backup writes a versioned manifest and includes server configuration with secret values excluded, context metadata and the normalized authoritative corpus, connector definitions without credentials, ai-memory persistent data, and index rebuild metadata. Enrollment and client credential state are excluded. Qdrant data is rebuildable and is never the only recoverable source.

Archives are created under a mode-0700 backup directory using a temporary name. They reject symlinks and special files and are atomically finalized. The CLI stops the managed gateway, context, and dependency services before backup or restore and starts them again afterward.

Restore stages and validates the format, bounded entry sizes, and safe paths; rejects links and traversal; and then atomically writes individual files without restoring secrets. It merges into managed roots and does not remove stale files. Restore accepts only an explicit local absolute archive path and is not exposed through the remote API.

Security invariants

The provider-neutral skill boundaries are implemented by a separate, versioned Skill Control Plane foundation. Registry, metadata-only indexing, dependency and conflict resolution, deny-by-default policy, and generic non-executing supply-chain staging are documented in skill-control-plane.md. The foundation is additive: the current session path does not require a populated registry and no external skill can assume orchestrator authority.

  • Secrets never enter the main TOML, URLs, diagnostics, or logs. ivoai does not inject stored secrets into subprocess argv; enrollment uses standard input or a no-echo prompt by default.
  • Installer and probe subprocesses use structured argv, bounded contexts, and minimal environments. Interactive agent subprocesses use structured argv and signal forwarding while retaining the compatible user environment required by the vendor clients.
  • HTTPS validation is on by default; no insecure-skip-verify production switch is persisted.
  • All optional layers have bounded timeouts or circuit breakers. They fail open only for launching the official local agent, never for authorization.
  • Connector and RAG data are untrusted and cannot invoke tools or mutate configuration.
  • Qdrant and internal admin surfaces are not public.
  • Enrollment is one-time, short-lived, cross-process locked, and atomically persisted. Client credentials are hashed server-side, scoped, and revocable.
  • Atomic writes reject unsafe ownership, modes and symlink targets.
  • Doctor redacts values and reports permissions, versions, reachability, TLS and protocol compatibility only.

Upstream decisions and known uncertainty

ComponentPinDecision and uncertainty on 2026-08-23
Codex CLI + code-mode host0.148.0Official same-release assets; codex login supports ChatGPT subscriptions and the separately checksummed host preserves the managed tool/MCP surface.
Claude Code2.1.228Official stable channel; latest was 2.1.237. Proprietary external binary subject to Anthropic terms.
Headroom0.36.0Current PyPI/GitHub release with amd64/arm64 wheels; fast-moving integration requires a setup smoke probe and direct fallback.
uv / CPython0.12.5 / 3.13.15Exact private installer/runtime pair for Headroom with embedded architecture-specific hashed constraints.
ai-memory1.29.0Newly released on the validation date, with changelog and per-arch hashes reviewed. Limited burn-in is mitigated by exact pin, hook timeout and failure isolation.
Ruflo3.38.12Exact npm integrity pin. Direct inference/provider routing remains unsuitable for the no-PAYG default and is disabled. Package/repository naming has moved historically, so updates require provenance review.
Qdrant1.19.0Current Apache-2.0 release and multi-arch OCI digest; internal only.
TEI1.9.3Mature CPU HTTP service; amd64 and arm64 upstream images are pinned independently by OCI index digest.
multilingual-e5-small614241f…Immutable MIT model revision; 384 dimensions/94 languages. Retrieval quality must be measured on the user's corpus before changing models.

No external account, private server, or real owner credential was used during this discovery. Compatibility with future upstream releases is not assumed. An ivoai release that changes a pin must revise this table and the manifest only after automated install, auth-status, wrapper, and failure-isolation tests pass.