AI platform · vevano — component status

The platform's own components · ai.vevano.vevano.no. Agents are tenant artifacts and are not listed here; observability lives in the shared foundation.

5up
0slow
0down
5not probed

Core

ComponentStatusEndpointTime
LLM observability (Langfuse)up · 200http://langfuse-web.langfuse.svc.cluster.local:3000/api/public/health6 ms
Agent runtime (LangGraph)up · 200http://langgraph.assistant.svc.cluster.local:8000/health5 ms
LiteLLM gatewayup · 200http://litellm.litellm.svc.cluster.local:4000/health/liveliness4 ms
MCP tool servers
speaks MCP, not HTTP health — no endpoint to probe
not probed——
Vector store (Qdrant)up · 200http://vevano-qdrant.qdrant.svc.cluster.local:6333/healthz6 ms
Embeddings (TEI)up · 200http://tei.tei.svc.cluster.local:8080/health6 ms

Sandbox

ComponentStatusEndpointTime
Credential broker
not deployed on this tenant yet
not probed——
Browser (Playwright)
no Service published by this release
not probed——
Agent workspace
verb/MCP surface only — no HTTP health route
not probed——

Board

ComponentStatusEndpointTime
HITL receiver
no Service published by this release
not probed——

How it is wired

The AI platform's wiring: one agent runtime in the centre, the platform's own components to its left, the foundation's shared services to its right, the tool servers below it, the systems a tool reaches at the edge, and — on the outside — the one inbound gateway that a human surface, a partner agent over A2A and a system webhook all arrive through wired in values todayendpoint exists, no address wired — or the feature is offa turn is proven through itrequest outQDRANT_URLTEI · unwiredLANGFUSE_*BROKER_URLMCPenqueue → runsandboxmodel providersthe only model egressLiteLLM gatewaykeys · aliases · budgetsintegrated in the shipped LG-ag runtime — proven liveagent runtimeengine is a seam:LangGraph today · Go · Rust opengraph · tools · promptscheckpoints in Postgresintegrated in the shipped LG-ag runtime — proven liveQdrantretrieval, user-scopedTEIembeddings · no consumer yetSandboxverb API · its own diskLangfusetraces + evals · own storesOTel collectorone per release todayAuthentikJWKS verified per turnintegrated in the shipped LG-ag runtime — proven liveCredential brokermints a scoped git tokeninbound gatewayproven: 202 · 200-duplicateintegrated in the shipped LG-ag runtime — proven livehuman surfaceTeams · board · consolepartner agentA2A · reads our cardsystem eventGitHub · e-sign · paymentMCP serversour tool packs · called liveintegrated in the shipped LG-ag runtime — proven liveBrowserChromium · MCPexternal systemsSaaS · their API, our tokeninternal systemsthe tenant’s own servicesThe foundation underneath — and what each telemetry store is FORSecrets: OpenBao → ESO → Secret → envFrom (a changed Secret does NOT restart a pod). Identity: Authentik, JWKS per request.METRICS collector :8889 → Prometheus → Grafana — “how fast, how often, how many errors?”TRACES collector → `debug` = printed, not stored · Langfuse is fed by the agent SDK — “what did it say, what did it cost?”categoriesagent runtimeAI platformfoundation servicetool server (MCP)sandboxoutside
One agent, and everything it needs. Left is ours — this product ships Qdrant, TEI, Langfuse and the sandbox, and the runtime reaches them by address the chart sets.Right is the foundation's — the OTel collector, Authentik and the credential broker are services every product uses, which is why they are a different colour and not part of this chart's own install. The broker is drawn there deliberately even though it is built in this repo today: the data platform and the application platform will mint their tokens from it too, so its home is the foundation, not this product. Up is the only way out — one straight line from the runtime through LiteLLM to the providers, so "where do model calls go" has exactly one answer.

Down is where the agent acts: MCP servers is the tool layer — the packs we ship — and the browser is one more server in that layer, drawn beside the systems a tool reaches, inside a pink sandbox frame. The nesting is the point: the browser is a tool by its interface (Chromium + @playwright/mcp: the agent seesbrowser_* in its tool list like any other tool) and a sandbox by its deployment — one disposable pod per session, its own namespace, an egress list that is empty by default and only grows when a human types a destination into the release, a TTL reaper. It is also the platform's largest untrusted-input surface, because it renders someone else's page, which is exactly why it is fenced rather than shared. Pink is the third colour: blue says whose thing it is, pink says what is quarantined.

One thing the arrow cannot say on its own: the edge from the tool layer meansthe browser is one of these, not the browser is proxied through the packs. Every MCP server is its own endpoint and the agent calls each directly — the box stands for the layer, because a picture with one box per pack would be a list, not a wiring.

The two grey boxes under the tools are what a pack would reach once a tenant names one — an external SaaS with its own API and the caller's token, or the tenant's own services inside the fence. Both are dashed on this tenant: nothing is wired to them yet, and the diagram says so rather than implying a capability we have not proved. The sandbox and the tool edges are dashed for the same reason.

Right is who calls us. Three kinds of caller run into ONE inbound gateway, because the platform must not learn which caller spoke — only that a verified one did: a human surface (a Teams message, a card action, a column move on the board), a partner agent over A2A, or a system event(a push, a signature, a settlement). A webhook and an A2A task are the same shape of thing and the same hazard: attacker-controllable text that will end up in a model's context, so the gateway verifies the sender, dedupes on their event id (they retry — twice is normal), maps it to an identity, a subject and an event, and enqueues. It ACKs in seconds and never runs the agent inline; the run happens off the inbox row. Every one of those four edges is dashed today: no inbound path is wired on this tenant yet.

A2A is half-built, and the figure says which half. The runtime already serves an Agent Card at /.well-known/agent-card.json, projected from the agent's own manifest, so a partner can discover what this agent does — that is the card the peer reads. What is missing is the inbound task endpoint and the per-caller policy, which is why the peer's edge is dashed. The task vocabulary is already in the runtime (TASK_STATE_INPUT_REQUIRED is how a parked human gate is projected onto the wire), so the same gateway that answers a person answers an agent.

What is NOT drawn, and why. The Cline runner and the surface it talked to are tenant artifacts, not platform components — drawing them here made the page look like a two-agent product, so they are gone. The gateway's measured negative stays true and is stated in the band: it exports spans to the collector but ships no traces to Langfuse, which is fed by the agent SDK.

Custom Surfaces

A surface is a purpose-built interface on its own Postgres — the shape the work already has, made clickable. An agent is defined with one of them: the surface is what gives the work a structure the agent can act on, and the agent is what makes the surface able to act back.

  1. the surface owns the data — It has its own Postgres and its own read model. The agent does not get a database connection.
  2. the agent acts through verbs — A narrow, declared set — task.move, task.schedule — checked per call against the verb table. Never SQL, never a generic (table, row).
  3. nothing runs inline — An action in the UI emits a webhook into the inbound gateway (verify · dedupe · enqueue). The run answers with a verb and a comment. A UI that waits on a model is a frozen UI.
Kanban boardGantt chart
the question it answersWhat state is this work in, and who owns it?When does this happen, and what blocks it?
what it projectscolumns = state · cards = tasks · a comment thread per cardrows = tasks on a time axis · bars = start…end · arrows = dependencies
what it owns (its Postgres)tasks(id, title, state, owner, updated_at) · task_commentstasks(id, title, start, end) · task_dependencies(from, to)
what the agent may calltask.move · task.comment · task.assigntask.schedule · task.link
what arrives as a webhookcard.moved · comment.addedtask.rescheduled · dependency.added
the failure it preventswork that moves without a record of who moved itdates that quietly violate a dependency

The list will grow — a calendar, a checklist, a spreadsheet — and a new surface is defined by the same three answers: what question it answers, what it projects, and which verbs the agent may call on it. What does not change is the wiring: the surface writes its own rows, the agent writes through verbs, and the surface talks to the agent only by emitting an event on the inbound seam.

Ideas

Agent runtime — who decides what

"Fully autonomous" is not a property of an agent, and it is not one dial either. Every agent shape is a different answer to three questions:(1) who picks each transition — your code, the model, or a check;(2) who says what steps exist — you at design time, or the model at run time;(3) who says stop. The figure colours the arrows for the first question, because the boxes are the same kind of thing in every lane: what makes an agent an agent is that some of its edges are blue.

Five patterns — a prompt chain, routing, an orchestrator with parallel workers, an evaluator-optimizer loop with a program as the judge, and a free agent loop — each drawn with the author of every arrow: your code, the model, or a check prompt chainno decision at allroutingthe model picks a branchorchestratorthe model names the workersevaluatorthe check loop (your case)agent loopthe free onestep 1extractstep 2rewritestep 3renderrequestany taskroutermodel pickspath Afixed stepspath Bfixed stepsrequesta big jobplannernames subtasksworker×1 of Nworker×2 of Nmergeone answergenerateone attemptcheckprogram, not modeldonethe passing oneanswerno tool callmodeldecides nexttoolsdeclared verbsyour code decidesthe model decidesa check decides
Lane 4 is your case: deterministic steps, and the only thing the model (or a check) owns is the exit. If the judge is a program — tests, a schema, a linter — the number of laps is computed from the input, so the run stays reproducible. If the judge is the model grading itself, the lap count is stochastic and correlated with its own blind spots. Either way the loop needs three things a straight line does not: acap on the cycles (counted separately from the step budget — a cheap judge with an expensive generator is a budget hole), a critique the next attempt canact on, and an escalation out when the cap is hit, so lap N is never returned as if it had passed.
patternwho picks each transitionwho says what steps existwho says stop
prompt chaincodecodecode
routingmodelcodecode
orchestrator–workerscode, in a workermodelmodel
evaluator–optimizercodecodecheck
free agent loopmodelcode (tools)model
the model writes the planmodelmodelmodel

The design conclusion is not "pick a level" — it is that these are four labelings of one engine. A node is a step, an edge is a transition, and every edge carries an author (code, model, check). Then a router, an orchestrator, a quality loop and a free agent are configurations, not architectures — same executor, same checkpoint, same accounting. Which is also what makes a framework question the wrong one: you are choosing a label per edge, not a platform for the whole agent.

MCP or a plain API — it is decided by clients, not by agents

This is the question that decides the shape of an integration, and it cannot be settled until three words stop being used as if they were one thing.

spec
The service's own description of itself — /openapi.json: paths, parameters, schemas, error shapes. It is not a tool, and the model never sees it. It belongs to the provider, it changes when the provider changes, and it is far wider than anything we want to expose: Flowcase's spec has hundreds of operations and this agent needs two.
tool
One thing the model may choose: a name, a sentence saying when to use it, a JSON schema for its arguments, and one verb. This is the model's whole vocabulary for acting, and it is re-sent every turn — so a large catalogue is a real per-turn cost. That is also why a tool is intent-shaped (submit_application), never a generic door (delete(table, row)): the second one makes the wrong action representable.
adapter
The code in between, and the only part that costs anything to own: it reads the spec, picks and narrows the two operations worth exposing, mints the tool schemas, maps auth and errors, and enforces the verb. It is a program in a process — therefore the entire questionMCP or OpenAPI is really whose process the adapter lives in: one copy per runtime, or one copy in a service they all call.

The instinct "one agent can call an OpenAPI service, but several agents need MCP" is wrong in its premise: several agents call the same OpenAPI service exactly as easily as one does. What scales with consumers is the adapter, so count runtimes — not agents. Cases A and B below are the same Flowcase integration with more agents and the same single adapter; the only thing that changes in case C is how many dashed boxes you keep alive.

The same Flowcase integration with one agent, three agents on one runtime, and three clients on three runtimes: one adapter, one adapter, then three adapters — or none at all if the clients speak MCP — or one shared adapter, lifted out of the runtimes into a gateway that any of the three can call over either wire case a · one agent, our runtimeagentthe CV agentadapter×1flowcase clientin-image · ×1Flowcaseone agent, one adapter — nothing to share, nothing to versioncase b · three agents, one runtimeagent 1screeningagent 2matchingagent 3schedulingadapter×1 · sharedlives in theharness onceflowcase clientshared moduleFlowcasethree graphs, ONE adapter — the cost did not grow with the agentscase c1 · three clients, three runtimes — over openapiLangGraphruntime 1adapter 1spec→toolsClineruntime 2adapter 2spec→toolspartner appruntime 3adapter 3spec→toolsflowcase APIthe specthree adapters — one per runtime, each kept in step with the same speccase c2 · the same three clients — over mcpLangGraphruntime 1Clineruntime 2partner appruntime 3flowcase MCP packone server · one credentialits own fence and versionno adapters — the clients already speak MCP; the pack holds the credentialcase d · one translator, lifted out of the runtimesLangGraphruntime 1Clineruntime 2partner appruntime 3flowcase gatewaythe one translator, sharedverbs · her bearer passed throughone fence · one versionFlowcasethe spec lives heremcp · the client already existshttp · you write 100 lines oncewhat gets copied: the translator, once — not once per runtime.what you still write: the client. ~100 lines, once per runtime, then reused for every tool.the axis is not the number of agents. it is where the one translator lives.
Case A and Case B are the same picture with more agents, and the work did not move: the adapter is written once, and three graphs become three subscriptions to it — a manifest line and a policy verb per agent, which is configuration, not code. That is why "several agents will use it" is not, by itself, an argument for MCP.

Case C is where the arithmetic changes, because there are three client programs to teach. Over OpenAPI that is three adapters, each re-tested when the spec moves; over MCP it is zero, because a LangGraph client, a Cline runner and a partner's app already speak the protocol.Note what did not change: Flowcase is reached once either way. The protocol is a property of the seam between the platform and its clients — not of the SaaS on the end.

Case D is the correction case C invites. "Three adapters" is only the price if the adapter stays inside each runtime. Nothing forces that: the translator is one program, so it can be lifted into a service the three runtimes call — and then the copy count is one again, which is where we started in case A. What the three runtimes still need is a client, and a client for a gateway you specified is a list/call pair — about a hundred lines, written once per runtime, and then reused unchanged for every tool you ever add, because the gateway already knows how to read a spec. So cases C2 and D are the same box: a shared, fenced, schema-carrying service. The protocol is the wire to it, not the architecture of it — and the honest reason to prefer the MCP wire is that somebody else already wrote the hundred lines for every runtime you might ever have, including one you write yourself.

So the decision rule, in the order it should be applied. Write the translator once, and never once per runtime. With one runtime that means in-process, which is also the cheapest thing to debug and the easiest to curl. With two or more it means a service — a gateway holding the spec→tool conversion, the verbs, the error mapping and the rate limit, with each runtime keeping only a generic list/call client.Choose the wire separately from the shape: stay on plain HTTP while the callers are ours (one client, ~100 lines, no protocol to adopt); speak MCP when a caller appears that you do not control and do not want to write one for — a partner's app, a no-code tool, a runtime someone else maintains. Choose a per-integration MCP pack (case C2) instead of a gateway when the integration ships on its own version, with its own credential and its own team — a release boundary, not a client count. For this scenario none of those triggers holds today, and one thing argues against a shared service holding the token specifically: the Flowcase credential is hers, so a server storing N users' tokens becomes a token-swapping proxy whose whole job is to never leak one session into another. A gateway that forwards her bearer — or a client that calls Flowcase directly — leaves that authorization where it already works: upstream.

What is free, and in which stack

The only practical question left after the atlas: for each thing a runtime needs, is itprovided, a library you wire (and own the policy for), orcode you write? Two shapes and three stacks, because the shape changes fewer rows than the stack does. ⬤ provided ·◐ library, you wire it · ✎ you write it ·▤ a manifest, not code · — not applicable at that shape. A ✓ in front of a name means the shipped LG-ag runtime implements it — LG-ag is in yellow because that is the column this product now occupies.

capabilityLG-wfLG-agGo-wfGo-agRs-wfRs-ag
execution skeleton
implemented in the shipped runtime. step scheduler incl. cyclesprovidedprovidedyou write ityou write ityou write ityou write it
implemented in the shipped runtime. conditional routingprovidedprovidedyou write ityou write ityou write ityou write it
parallel fan-out + joinprovidedlibrary, you wire ityou write ityou write ityou write ityou write it
subgraphs / subagent delegationlibrary, you wire itprovidedyou write ityou write ityou write ityou write it
implemented in the shipped runtime. the model ↔ tool loop itselfnot applicableprovidednot applicableyou write itnot applicableyou write it
implemented in the shipped runtime. provider tool-call parsing + schema bindinglibrary, you wire itprovidedyou write ityou write ityou write ityou write it
implemented in the shipped runtime. streaming (text and tool deltas)library, you wire itlibrary, you wire ityou write ityou write ityou write ityou write it
implemented in the shipped runtime. per-run cancellationlibrary, you wire itlibrary, you wire itprovidedprovidedlibrary, you wire itlibrary, you wire it
implemented in the shipped runtime. step capprovidedprovidedyou write ityou write ityou write ityou write it
durability
implemented in the shipped runtime. checkpoint store + resumeprovidedprovidedyou write ityou write ityou write ityou write it
HITL park / resumeprovidedprovidedyou write ityou write ityou write ityou write it
replay / eval fixtures from a tracelibrary, you wire itlibrary, you wire itprovidedprovidedprovidedprovided
idempotency on resumeyou write ityou write ityou write ityou write ityou write ityou write it
context — where the quality comes from
implemented in the shipped runtime. context budgeter (tokens per section)not applicableyou write itnot applicableyou write itnot applicableyou write it
implemented in the shipped runtime. compaction + working-set pinningnot applicablelibrary, you wire itnot applicableyou write itnot applicableyou write it
implemented in the shipped runtime. tool results → artifact refslibrary, you wire itlibrary, you wire ityou write ityou write ityou write ityou write it
skill catalogue / progressive disclosurenot applicableyou write itnot applicableyou write itnot applicableyou write it
memory retrieval with provenancenot applicablelibrary, you wire itnot applicableyou write itnot applicableyou write it
implemented in the shipped runtime. dedup + cache-friendly orderingnot applicableyou write itnot applicableyou write itnot applicableyou write it
authority
implemented in the shipped runtime. per-call policy / verb tableyou write ityou write ityou write ityou write ityou write ityou write it
implemented in the shipped runtime. budget ledger — steps · tokens · cost · cyclesyou write ityou write ityou write ityou write ityou write ityou write it
depth + concurrency capsyou write ityou write ityou write ityou write ityou write ityou write it
implemented in the shipped runtime. kill switchyou write ityou write ityou write ityou write ityou write ityou write it
credential brokering (short-lived)you write ityou write ityou write ityou write ityou write ityou write it
platform
sandbox pod supervision + k8s clientlibrary, you wire itlibrary, you wire itlibrary, you wire itlibrary, you wire itlibrary, you wire itlibrary, you wire it
implemented in the shipped runtime. egress / NetworkPolicya manifesta manifesta manifesta manifesta manifesta manifest
trace emitter → Langfuseprovidedprovidedyou write ityou write ityou write ityou write it
a new edge kind is compiler-checkedyou write ityou write ityou write ityou write itprovidedprovided
build / iterate cyclenot applicablenot applicableprovidedprovidedlibrary, you wire itlibrary, you wire it
implemented in the shipped runtime. no dependency resolution in the shipped imageyou write ityou write itprovidedprovidedprovidedprovided
rows you write, of 3081118241723
✓ implemented in the shipped LG-ag runtime17 of 30 rows
rows the stack provides794433

The numbers in the footer are computed from the table, not typed in — and they make the point sharper than an argument would: 6 of 30 rows are ✎ in all six columns, and those are the ones that decide whether an agent is good (context discipline) or safe (budget ledger, verb table, caps, kill switch, brokering, idempotency). Choosing the LangGraph agent over the Go agent moves18 of 30 rows — but every one of them is tedious-but-known work, and not one is in those 6. That is why the stack question is real but not urgent: the 6 are the design, the rest is typing.