Everything missing between a model and production.
Every feature in one sentence for decision makers, the details for your platform team, and a recording from the real interface.
PII vault
Personal data never leaves the building.
Before any text reaches an AI model, ATF replaces names, IBANs, emails, phone numbers and health details with meaningless placeholders – and puts the real values back only in the answer your staff sees.
For your platform team
- Reversible
[[PII:…]]tokenization at the engine decorator – applies to chat, jobs, triggers, replay and flow-as-MCP alike - Detector pipeline: regex + checksums (IBAN, tax ID, social insurance number, VAT ID), in-process NER, an external Presidio-compatible service
- Per-execution mapping encrypted with AES-256-GCM, bound to execution ID and token (AAD)
PII_DETECTOR_FAIL_MODE=closed: if a detector fails, the request does not go out- Multimodal policy for images (block, redact, describe) – explicit, never implicit
- Configurable per flow; MCP tools see placeholders too
MCP Gate
Governed tools instead of full access.
The Gate is the gatekeeper between AI and your systems: it only offers the actions you allow in a configuration file – and every write action can require a human.
For your platform team
- Tools from YAML: SQL (PostgreSQL, MySQL and more), HTTP, MQTT, webhook, MCP proxy, composite – no code
- Parameters are always bound, never interpolated into SQL; the verified identity flows into the query
- Policy rules
allow | deny | approvalper tool, access type, scope, group or argument - Server-driven approvals via MCP 2026, distributed through Redis, with requester/approver separation
- Separate read and write paths (
ask/act): a read intent never reaches a mutating tool - Live monitor, tool playground and audit log; tool content shown only as a SHA-256 digest by default
Human approval
Silence never becomes an action.
When an agent wants to move money, delete data or book something, ATF stops and asks a human. If nobody answers in time, the action is rejected – never silently executed.
For your platform team
- Execution pauses with tool, tenant and planned arguments – credentials are never part of the request
- Two decision surfaces: an approval element in the chat answer and an RBAC-protected inbox in Control
- Fail-closed:
APPROVAL_TIMEOUT_MSelapsed = rejected - Cluster-wide visibility with Redis; the approver comes from the verified identity
- Optional four-eyes rule (
requireDifferentApprover) with its own scope - Every decision, including timeouts, in the audit log and as a metric
End-to-end identity
The user stays the user – down to the last database row.
Whoever asks in the chat is passed through to the target system with their real sign-in. An agent can therefore never see or do more than the person it works for – and every system knows whether a human or an agent is acting.
For your platform team
- OIDC for end users and operators, JWT/JWKS with multiple issuers and issuer-to-tenant binding
- Passthrough:
forwardAuthTokenforwards the verified user token to MCP servers - Token exchange per RFC 8693: one token per target system with its own audience and reduced scope
- Delegation with an
actclaim – target systems such as the Gate can tell a direct user action from an agent acting on their behalf - Transaction token per tool call, asymmetrically signable (ES256/EdDSA) and limited to exactly one service by audience
- Search and Gate only use the verified identity – never a field the client sends along
# FLOWS_DIR/mcp.yaml – one MCP server, user identity end to end
servers:
- name: erp
url: https://erp-gate.internal/mcp/v2026
transport: http
profile: "2026-07-28"
forwardAuthToken: true # pass the verified user token on …
tokenExchange: # … exchanged per RFC 8693
issuer: https://sso.example/realms/corp
clientId: atf-runtime
clientSecretEnv: MCP_TX_SECRET # name of the env var, never the secret
audience: erp-gate # token valid for this system only
scope: "openid erp:read erp:write"
actor: # delegation: act.sub = Runtime
grant: client_credentials
clientId: atf-runtime-actor
clientSecretEnv: MCP_TX_ACTOR_SECRETSigned evidence
Compliance is evidence, not assertion.
Every execution can be exported as a digitally signed evidence package. Your auditor verifies it on their own laptop – without access to your platform and without having to trust you.
For your platform team
- Evidence bundle: execution, inputs, policy decisions, sources and answer, Ed25519-signed
- Public key as JWKS at
/.well-known/atf-evidence-jwks.json– no shared secret atf-verifychecks bundles, decision receipts (COSE_Sign1 + Merkle proof), erasure receipts, release receipts and audit exports offline- Hash-chained audit log with signed anchors; tampering is detectable
- Debug trace with
flow_version,flow_digestand subflow versions – what ran is unambiguous - Strict algorithm branching: never a silent fallback between Ed25519 and HMAC
Policy as code
Rules that apply before execution.
Which data may go to which provider, which actions need an approval and how long anything is kept is written in a reviewable file – and ATF enforces it before a model is called.
For your platform team
compliance-policy.yaml: data classes, provider regions, model allow/block, approvals, retention, audit- Invalid policy at startup = chat answers 503 instead of running without rules
- Dry run and policy replay against stored executions in Control
- Erasure across all services (GDPR Art. 17) with a signed receipt and an honest
partialstatus - Generated compliance dossier: policy, chain verification, erasures, retention, evals, red-team score
- DPIA and record-of-processing templates plus an EU AI Act mapping in the repository
Flows & Studio
AI workflows you can review.
What an assistant or agent does is a plan in a text file. Business and IT see it as a graph in the Studio, test it with one click and change it like source code – with a version number.
For your platform team
- Twelve built-in node types: llm, tool, router, classifier, parallel, retrieval, memory, agent-harness, transform and more
- Conditional edges through a safe expression evaluator – no embedded code
- Hot reload from
FLOWS_DIR, JSON schema validation, CI-enforcedmetadata.versionbumps - Studio: canvas, prompt editor, variables, drafts, copilot proposals as an overlay, run & trace per node
- Flow as MCP server, webhook and schedule triggers, async jobs with lease recovery
- Release gate with a bench file and an Ed25519-signed release receipt
Agent harness
Multi-step agents with a budget and a reviewer.
For complex questions a planner splits the task into steps, hands them to specialised building blocks and re-plans with the results – until a reviewer accepts the outcome or a budget is reached.
For your platform team
- Planner/worker loop: delegates only to nodes on an
allowlist, never to arbitrary tools - Hard limits:
maxIterations,maxCost,maxTimeMs, delegation depth, parallel tasks - Verification by a reviewer node with a pass signal, optionally fail-closed
consequentialTargets: delegations to consequential nodes pause for human approval- Plannerless mode with a deterministic coverage queue for exhaustive research over a corpus
- Progress as an
atf.progressevent with phase, coverage and ETA – in the stream, trace and job API
- id: research
type: agent-harness
label: Recherche-Agent
config:
allowlist: [search_contracts, search_mail, summarize]
maxIterations: 8
maxCost: 0.50
maxTimeMs: 180000
maxConcurrentTasks: 3
consequentialTargets: [notify_customer] # pauses for approval
verification:
target: reviewer
passSignal: APPROVED
failClosed: trueGuided decoding & structured output
Answers in the shape your system expects.
Where an answer is processed further – a classification or a record, say – ATF forces the model into a fixed format. No “almost right” JSON that breaks your system.
For your platform team
outputSchemaandclassifier: JSON schema via native structured output (response_format: json_schema) – constrained sampling on vLLM, LM Studio and cloud providers- Fallback without native support: schema hint plus a tolerant parser
- Model pools rotate to the next model on an unparseable answer, otherwise
config.fallbackapplies - Reasoning models: JSON that lands in the thinking channel is detected and taken over safely – without leaking chain-of-thought
finalAnswerContract: only the text between two markers is the answer; plans and notes are dropped- Classification drives routing through conditional edges – no embedded code
- id: triage
type: classifier
classifierSchema:
classKey: intent
schema:
type: object
properties:
intent: { type: string, enum: [claim, question, complaint] }
urgency: { type: string, enum: [low, normal, high] }
required: [intent, urgency]
- id: extract_claim
type: llm
config:
outputSchema:
name: claim
schema:
type: object
properties:
policyNo: { type: string, pattern: "^[A-Z]{2}-\\d{6}$" }
amount: { type: number, minimum: 0 }
required: [policyNo, amount]Search
Enterprise RAG on PostgreSQL.
ATF finds the passages in your documents an answer has to rest on – and only shows documents the person asking is allowed to see.
For your platform team
- Hybrid: pgvector dense + full text, fused with reciprocal rank fusion; optional SPLADE sparse
- Reranking, HyDE, semantic and late chunking, contextual prefixes – each switchable
retrieve_for_answer: sub-questions, fusion, dedup, token budget, passages labelled untrusted- Access by verified identity and groups, tenant-separated via row-level security
- Collections with their own language configuration, stable score-keyset pagination
- Embedding drift protection: a model change without reindex fails loudly
Document versions & time travel
What applied on 1 March – and what has changed since?
Policies, contracts and manuals change. ATF keeps every version and, on request, answers as things stood on a given date – and shows what changed between two dates.
For your platform team
X-Valid-Fromper document, queries withX-Valid-At(state at an instant) orX-Include-All-Versions- Version order numeric (
1.10 > 1.9), then by date – an older version never becomes the current one document_diff: sections added, removed or changed between two instantstrace: follows an identifier (e.g. a clause ID) across two instants through all documents- Atomic revision switch; progressive revisions are searchable while still processing
- The same
valid_atcontract in retrieval eval and the MCP tools – with the same per-document permissions
# Current version
curl -H 'X-Collection: policies' --data 'retention' https://search.internal/search
# As it stood on 1 March 2026
curl -H 'X-Collection: policies' \
-H 'X-Valid-At: 2026-03-01T00:00:00Z' --data 'retention' https://search.internal/search
# What changed in one document between two dates
GET /v2/documents/policies:retention/diff?collection=policies
&from=2026-01-01T00:00:00Z&to=2026-10-01T00:00:00ZExtract
Documents a model actually understands.
ATF turns PDFs, office files, scans and emails into cleanly structured text with pages, tables and figures – so answers point to the right page.
For your platform team
- PDF, DOC/DOCX, XLS/XLSX, PPTX, VSDX, HTML, images, BPMN 2.0, EML/MSG/mbox
- The text layer is authoritative for wording, the rendered page for structure
## Seite Npage markers, figures with geometry, tables with merged cells- Hardened against hostile documents: ZIP limits, no macros, isolated converter, prlimit
- Page windows, job lanes, derived-PDF cache, daily vision page budget
- Quality eval (CER, heading F1, TEDS) as a gate
E-mail threads & deduplication
Mailboxes without every reply dragging the whole history along.
ATF reads e-mails and whole mailboxes, assigns replies to their conversations and removes quoted history that is already present. Answers then rest on each statement exactly once – instead of on ten copies of the same mail.
For your platform team
- EML, MSG (Outlook) and mbox natively, without a model call; an mbox is split into individual messages
- Signatures are removed; quoted history only when the quoted message is already in the collection – otherwise it is kept as “quoted history”
- Inline replies keep exactly the lines they answer
- Threading via Message-ID, In-Reply-To, References and the Outlook Thread-Index; attachments join the thread
- Duplicates by SHA-256, text hash and Message-ID, near-duplicates by MinHash – only within the same permissions
- Hits grouped per thread;
get_threadreads a conversation chronologically with sources per message
# A mailbox export: one document per message, threads linked
curl -X POST https://search.internal/ingest-file \
-H 'X-Collection: support-mail' \
-H 'X-Reference: postfach-2026-10.mbox' \
-H 'Content-Type: application/mbox' \
--data-binary @postfach-2026-10.mbox
# One hit per conversation instead of ten copies of the same mail
curl -H 'X-Collection: support-mail' -H 'X-Group-By-Document: true' \
--data 'Lieferverzug Auftrag 4711' https://search.internal/searchOutpost
Documents come to ATF – not the other way round.
A small sync agent runs on your side – on the file server or as a desktop app – and pushes folders, S3, Google Drive or Dropbox into the platform. The cloud never gets read access to your sources.
For your platform team
- One sync engine (
outpost-core) for the Docker daemon and the desktop app - Mirroring with a brake: large delete sets pause until the exact candidate hash is confirmed
- Least privilege: read-only scopes, only
extract.ingest - Desktop: tokens in the OS keychain, the WebView never sees credentials
- Every secret also as
_FILE; the logger scrubs registered secrets - Health endpoint for monitoring
# /config/outpost.yaml
extractUrl: https://extract.internal
collection: handbook
syncIntervalSec: 900
mirrorDelete: true # deletions propagate …
deleteThresholdPercent: 20 # … large sets wait for confirm-deletes
sources:
- id: werk-handbuecher
type: local
path: /sources/handbuecher
allowedGroups: [instandhaltung]
- id: qm-bucket
type: s3
bucket: qm-dokumente
region: eu-central-1Guardrails & red teaming
The return channel is checked too.
ATF checks not only what users type but also what comes back from documents and tools – because that is where attackers hide their instructions. And it attacks itself regularly.
For your platform team
- Input and output guardrails with pattern and model detection
- Tool output validator scans MCP results and RAG chunks for indirect prompt injection
- Taint across service boundaries: after a finding every non-read tool needs an approval
- Spotlighting: retrieved passages reach the model as a marked untrusted block
- Declarative tool authorization, fail-closed; egress policy for tool traffic
- Continuous red teaming with an attack library and a posture score
Observability
Every answer has a résumé.
For every AI answer you see which workflow ran in which version, which sources and models were involved, how long it took and what it cost.
For your platform team
- OpenTelemetry traces, Prometheus metrics, structured logs with redaction
- Per-execution debug trace with node timings, model calls and policy decisions
- Live graph over SSE/WebSocket with ticket authorization
- Explain card: model, sources, policy, routing – as Markdown or signed bundle
- Governance posture report: which controls are active, as ok/warn/fail
- SLO tracking and alert webhooks
Cost & reliability
Predictable AI instead of a surprise invoice.
ATF knows what every request costs, stops at budget limits and fails over to another provider automatically when one goes down.
For your platform team
- Cost per tenant, day, model, user and flow; forecasting and CO₂ accounting
- Hard daily budgets and per-run budgets (
maxTokens,maxToolCalls,maxWallMs) - Objective-driven routing and model cascade: cheap first, escalate on weak evidence
- Fallback chains with circuit breakers, model pools, sticky pools for prefix caches
- Three cache layers; idempotency prevents duplicate actions
- Priority admission queue, rate limits per identity and user
Quality: eval, canary, shadow
Measure changes before users feel them.
Before a new version of an assistant goes live it is tested against real examples, rolled out to a share of requests first, or tested invisibly in parallel.
For your platform team
- Eval datasets, judge scoring, matrix runs; structured assertions (
toolCalled,jsonPath, …) - Canary promotion and rollback by criteria
- Shadow mode on real traffic – users always get the stable answer
- Replay of earlier executions; shadow and replay never repeat side effects
- Self-optimizing loop behind a canary gate, bounded by a drift guard
- Groundedness checks against the retrieved sources
# flows/bench/support-rag.bench.yaml
version: 1
flow: support-rag
thresholds:
passRate: 0.95
maxLatencyMsP50: 4000
cases:
- id: filter-interval
messages:
- { role: user, content: "Wie oft muss der Filter gewechselt werden?" }
assert:
- { type: contains, value: "500" }
- { type: toolCalled, name: "vectorsearch/search", min: 1 }
- { type: noDiagnostic, code: loop_blocked }
$ bun run scripts/cli.ts flows release support-rag \
--signing-key-file release.pemTenants & access control
Who asks decides what is visible.
Departments, subsidiaries or customers share one platform without seeing each other's data – enforced in the database, not just in the interface.
For your platform team
- Tenant separation with PostgreSQL row-level security (
TENANT_RLS_REQUIRED) - JWT/JWKS with multiple issuers, issuer-to-tenant binding, OIDC with a backend session
- Trusted identity headers only from configured sources (
TRUSTED_IDENTITY_HEADERS) - BYOK: provider keys per tenant, encrypted
- RBAC from token claims; agent identity broker with revocation
- One operating profile
ATF_PROFILE=productionrefuses unsafe starts
ATF_PROFILE=production
AUTHN_MODE=token
JWT_JWKS_URL=https://sso.example/realms/corp/protocol/openid-connect/certs
JWT_AUDIENCE=atf-runtime
JWT_ISSUER_TENANTS={"https://sso.partner.example":["partner-a"]}
TENANT_ISOLATION_ENABLED=true
TENANT_RLS_ENABLED=true
TENANT_RLS_REQUIRED=true
BYOK_ENABLED=trueOpenAI-compatible API
Existing clients, new control.
Existing chat interfaces and applications talk to ATF as they would to a familiar AI service – except that all your rules now apply in between.
For your platform team
/v1/chat/completions,/v1/responses,/v1/models,/v1/embeddingswith streaming- Providers: OpenAI-compatible, Anthropic, Google, Azure OpenAI, AWS Bedrock, Ollama, LM Studio, vLLM
- Labelling of AI-generated answers (
AI_DISCLOSURE) - Signed webhooks and a Rocket.Chat channel with a transactional outbox
- Contract packages with compatibility guarantees and OpenAPI specifications
- Docker Compose quickstart with a mock model – no API key
curl https://atf.example/v1/chat/completions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "support-assistant",
"stream": true,
"messages": [{"role": "user", "content": "Status von Ticket #4711?"}]
}'Open WebUI & chat front ends
The familiar chat interface – with every rule behind it.
Staff use a familiar chat interface such as Open WebUI. Every released workflow appears there as its own “model”; sources, approvals and progress show up directly in the chat.
For your platform team
- Flows appear as models via
/v1/models; streaming including a collapsible reasoning block - Sources as native, clickable citations – only those the answer actually references with
[n] - Approval element right in the chat (embedded,
APPROVAL_FRAME_ANCESTORSlimited to the Open WebUI origin) - Identity from Open WebUI: JWT via the OAuth cookie or pipe metadata, a separate JWKS for the inference API
- Open WebUI knowledge and functions as client tools – switchable per node (
clientTools) - Chat history clean-up: UI artefacts of earlier answers never reach the model as a “worked example”
# Open WebUI → Admin → Connections → OpenAI API
URL: https://atf.example/v1 # every released flow is a "model"
# Runtime
APPROVAL_FRAME_ANCESTORS=https://chat.example # approval widget in the chat
APPROVAL_BASE_URL=https://atf.example
MEDIA_BASE_URL=https://atf.example # generated media as links
JWT_JWKS_URL=https://sso.example/.../certs # user token from Open WebUI