Repository navigation
Description
Current scope (2026-11-06): This closed issue preserves the historical specification and research. #577 now scopes GTC DC P0 to OpenClaw and Hermes. Deep Agents Code adapter, cold resume, mounted skills and qualification are deferred until after GTC DC in #566; historical dcode P0 references below do not restore that delivery commitment. Shared Fabric lifecycle and transport requirements remain applicable. LangChain Deep Agents is unaffected.
Closed on 2026-11-02. GTC DC P0 work is tracked in #577, and deferred work, including #565 and #550 to #560, in #566. The P0 specification below still applies; #578 (cold resume) and #579 (OpenClaw's and Hermes's terminals) extend it.
Affected area: Harness adapter; Schemas or configuration; Consumer integration; Documentation or examples
How to read this issue. This description is the specification. It consolidates the decisions, clarifications and reviews that were posted as comments; those comments are hidden as outdated, and the change log records what changed. To change the specification, edit this description and add a change-log line. Sub-issues hold only their adapter's details.
Problem or opportunity
Consumers that run agents for people, such as NVIDIA NemoClaw, want to offer each harness's own interfaces: an API, a browser dashboard with chat, or a terminal UI. Fabric's adapters configure and supervise the harness, so they are the only place that can turn those interfaces on safely. Answered separately, each adapter would get a different shape for settings, credentials, ports and health, and each consumer would need adapter-specific code to reach them. This epic defines one contract and leaves each adapter's specifics to its sub-issue.
| Sub-issue | Priority | Harness | Interfaces | Adapter work | Effort |
|---|---|---|---|---|---|
| #541 | P0 | OpenClaw | Control UI (browser dashboard and chat) | A setting on the managed runtime's Gateway, plus its per-start token file; already shown working with a one-line change | Small |
| #540 | P0 | Hermes | API server, dashboard, browser chat | Runtime-bound Hermes API server and dashboard children; the service mode is deferred (#566) | Medium |
| #546 | P0 | Deep Agents Code | Terminal UI, optionally ACP | A new adapter around dcode's agent server |
Large |
| #550 | Negotiable | Kilo Code | Terminal UI, ACP | Report the kilo serve the adapter already runs, so kilo attach opens the terminal UI on it |
Small |
| #551 | Negotiable | Codex | Terminal UI | Open the terminal UI on the runtime's app-server or a shared app-server daemon | Medium |
| #552 | Negotiable | OpenCode | Web UI, terminal UI | Depends on what OpenCode v2 provides; otherwise a service mode | Medium |
| #553 | Negotiable | Qwen Code | Terminal UI, ACP | Attach to the runtime's session, through qwen serve if available |
Medium |
| #554 | Negotiable | Droid | Terminal UI | Attach to the runtime's session, through droid daemon if the SDK uses it |
Medium |
| #555 | Negotiable | Claude Code | Terminal UI | Open the terminal UI on the runtime's session, for example as a background session | Medium |
| #556 | Negotiable | Pi | Terminal UI | Open the terminal UI; sharing needs a session file instead of memory | Medium |
| #557 | Negotiable | Cline | Terminal UI, ACP | Open the terminal UI with the runtime's configuration, attached if the core allows | Medium |
| #558 | Negotiable | Kiro | Terminal UI | Open the terminal UI with the runtime's configuration, on its session if Kiro allows | Medium |
| #559 | Negotiable | Strands | Interactive CLI | Open the interactive CLI with the runtime's configuration, on its session if Strands allows | Medium |
| #560 | Negotiable | OpenHands | Agent server API, browser UI | A service mode running openhands-agent-server |
Medium to large |
| #565 | After P0 | All | Discovery and shared code | Generalize from the P0 adapters | Medium |
| #566 | Deferred | All | – | Deferred contract items | – |
Not covered: the Deep Agents SDK adapter has no native interface (#546 covers Deep Agents Code); mini-swe-agent offers only an interactive terminal and a trajectory inspector; NOOA's nooa start-dev is a trace and evaluation viewer, not an agent console; and the remote-agent adapter's remote service owns its interfaces.
Status
The P0 specification below is confirmed by the epic's author, including #540's runtime-bound scope, and two specification reviews judged it implementable. An adversarial review then raised nine questions, which the epic's author settled on 2026-10-30; the requirements below include the answers, and Resolved questions records them. A Fabric maintainer can still object; until one does, implementers build against this text.
Reference implementation. NemoClaw's team builds the P0 adapters in cv/NeMo-Fabric to validate this specification and unblock NVIDIA/NemoClaw#14011. The Fabric team can review, cherry-pick or merge from it at its own pace. Gaps found while building it are settled by editing this description.
P0 specification
Applies to #541 (OpenClaw), #540 (Hermes) and #546 (Deep Agents Code). The IDs are for tests and reviews; a requirement marked with a question (Qn) was settled by that question's answer below.
Settings
- S1.
harness.settings.interfacesis an object of named interfaces. Absent means off; present means on unlessenabled: false;enableddefaults totrue. - S2. HTTP interfaces require
port: an exact loopback port with no default, so the consumer always knows it. Before launching a child,startchecks that nothing listens on its port; after the child passes health,startconfirms that the child is still running and owns the listening socket. Otherwisestartfails because the port is taken (Q5). The ownership check reads Linux procfs; on other platforms, only the check before launch applies. Adapters with derived ports state which ones they check and which they reserve (Q7). - S3. Two interfaces of one runtime can't share a port. Equal ports fail
start; planning doesn't catch them, because JSON Schema can't compare sibling values. - S4. Terminal interfaces take no
port. - S5.
public_origins, for browser interfaces that support it (OpenClaw'sdashboardin P0; not Hermes's), is a list of origins (scheme://host[:port]) accepted in addition tohttp://127.0.0.1:<port>andhttp://localhost:<port>. OpenClaw accepts any loopback origin regardless of this list, with its token still required, so a consumer doesn't need to list a different local forward port;public_originsmatters for non-loopback origins (Q6). - S6. Adapters may add interface-specific fields.
- S7. Names in P0: OpenClaw
dashboard; Hermesapianddashboard; Deep Agents Codeterminal. - S8. Each P0 descriptor lists exactly its supported names under
settings_schema.properties.interfaces.properties, withadditionalProperties: falsethere and on each interface object. Fabric's existingsettings_schemavalidation then rejects unsupported names and fields during planning, and a consumer can check the image catalog's descriptor before deploying. - S9. There is no
optionalfield in P0; an enabled interface that can't start always failsstart([Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566).
Lifecycle
- L1. Interfaces live with a managed runtime, because consumers that drive agents through
fabric-agent-ctlhave no service operations. Services (prepare_service(),attach_service()) rejectinterfaceswith a stable error before starting anything; service binding is [Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566. - L2. An interface can't start when its child process exits, doesn't pass its health endpoint within the startup timeout, or fails S2's port check.
startthen fails, stops the children it already started, and leaves no token files or markers (Q1, Q5). - L3.
startup_timeout_secondsis one deadline for all ofstart: children start concurrently and must all pass health within it. Default 60, maximum 75, which leaves time for cleanup within core's 90-second limit on an adapter'sstart. Adapters without a startup timeout gain this setting; OpenClaw's existing one also became one deadline for all ofstart, with the same default and maximum (Q3). - L4. An interface that is the agent's own server is covered by the runtime check. A separate interface process never fails the runtime check, and Fabric doesn't restart it.
- L5.
stopfirst removes the runtime's token files and interface markers, then sends SIGTERM to each interface child's process group, waits up to 5 seconds, and sends SIGKILL to what remains; in-flight interface requests are cut off. Where the harness has its own shutdown drain, the adapter configures it below 5 seconds (Q4). - L6. A consumer-chosen port stays fixed across restarts; the adapter fails rather than choosing another.
- L7. If core kills or loses the adapter host (a crash, or core's start or stop limit), nothing removes the runtime's token files and markers in P0, and processes the adapter didn't start as its children aren't tracked (A4). Each adapter's README documents this (Q2, Q8).
Paths
- P1.
<artifact root>is the runtime's Fabric artifact root:RuntimeContext's artifact directory, or exactly<base>/artifactswhen it's unset, regardless of where an adapter keeps its other files;/sandbox/artifactsin NemoClaw. - P2.
<adapter>isopenclaw,hermesordeepagents-code. - P3. Tokens are at
<artifact root>/.fabric/<adapter>/runtimes/<runtime-id>/interfaces/<name>.tokenfor interfaces that have one: OpenClaw'sdashboardand Hermes'sapi. Hermes's dashboard and Deep Agents Code's terminal have none. - P4. Deep Agents Code's terminal launcher is
.../interfaces/terminal, with its marker.../interfaces/terminal.running. The launcher checks only the marker, so after L7 it can open a session against the old runtime's configuration until the sandbox is replaced (Q2). - P5. These paths stay valid after [Enhancement]: Interfaces: generalize discovery and shared adapter code from the P0 adapters #565, whose handle fields report the same locations.
Credentials and output
- T1. The adapter generates each token at every start and writes it owner-only (
0600) only after every child passes health, as it does markers (Q1); a child that needs its token at launch receives it in its environment. Tokens never appear in handles, plans, results or logs. - T2. Consumers keep tokens out of desired-state files, plans, deployment state, their own command output and logs, and test artifacts, and print the command that retrieves a token rather than the token. A person's explicit retrieval, for example
openshell sandbox exec -n SANDBOX -- cat <path>, is the intended disclosure to that person. - T3. The agent's own tools can read the token. In a NemoClaw sandbox,
openshell sandbox execruns as the harness's identity, so owner-only files are readable, though another process's environment is not. P0 documents this limitation; verifier credentials are [Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566. - T4. Each adapter documents how a person signs in, for example OpenClaw's
#token=URL. Generic discovery ([Enhancement]: Interfaces: generalize discovery and shared adapter code from the P0 adapters #565) locates interfaces and credentials; applying a credential stays adapter-specific until [Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566's scheme fields.
Access
- A1. Interfaces listen on loopback only. Exposing them is the consumer's job, for example with OpenShell's
forward service. - A2. Discovery is by convention: the consumer chooses the port, and finds the token at P3 using the runtime ID that
fabric-agent-ctl startreturns. Handle fields and a read-only status command come in [Enhancement]: Interfaces: generalize discovery and shared adapter code from the P0 adapters #565 (fabric-agent-ctl: read-only status command for the owned runtime #420). - A3. Terminal interfaces are reached with
openshell sandbox exec -n SANDBOX --tty -- <launcher path>.openshell sandbox connectattaches to the sandbox's main process, which in NemoClaw isfabric-agent-ctl serve, not a shell;sandbox exechas no-tshort form. - A4. Hermes's dashboard has no sign-in in P0. Hermes's dashboard (v0.22.4 and v0.22.5) embeds its session token in the page it serves, and that token only gates direct API calls; a non-loopback
dashboard.public_urlswitches on Hermes's provider-based auth gate, which refuses to start without a configured provider. So the dashboard is reached only through a gateway-authorized forward, which is its access control, with no dashboard token and nopublic_origins; its README says anyone who can reach its port, including the agent's own tools, can use it. Exposed service URLs for it wait for [Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566. Hermes's API server keeps its Bearer token. The dashboard's gateway actions, such as restart and migrate, start detached Hermes processes that replace the Fabric-started gateway and can outlivestop; P0 documents this (Q8).
Configuration and behavior
- C1. The adapter generates the harness's configuration from
FabricConfigat each start. Every harness process that calls a model, including interface children, usesFabricConfig's endpoint and the key from itsapi_key_env, set in that process's configuration only (Q9). Changes made through an interface aren't supported and don't survive a restart; each adapter documents this. - C2. Conversations in an interface aren't promised to share threads with Fabric invocations; each adapter documents its behavior.
- C3. Update checks, automatic updates and telemetry that an interface's harness makes on its own stay off unless configured.
- C4. Each README documents what an environment needs for its interfaces (packages, built frontends, binaries) and the interface features that would let the agent bypass its guardrails, such as approvals, configuration edits or plugin installation; disabling them is [Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566.
Resolved questions
An adversarial review reproduced findings 1 to 8 against the pinned harnesses and Fabric core, and a TLA+ model of the lifecycle raised Q1. The epic's author settled all nine on 2026-10-30.
| ID | Question and evidence | Decision | Requirements |
|---|---|---|---|
| Q1 | Does a failed start remove the token it already wrote? (model) |
Tokens and markers are written only after every child passes health, so a failed start leaves none. |
L2, T1 |
| Q2 | Who removes tokens and markers when core kills or loses the adapter host, and how does the separate-session launcher know the runtime is live? Core's stop reports success after a crash, the files stay, and a restart uses a new runtime ID. (findings 3, 4, 7) |
Accepted for P0 and documented: the files can stay, and the launcher, which checks only the marker, can open a session against the old configuration until the sandbox is replaced. | L7, P4 |
| Q3 | Two children started one after the other, each healthy after 50 seconds, exceed core's 90-second start limit. (finding 4) |
One deadline for all of start; children start concurrently; default 60, maximum 75. |
L3 |
| Q4 | Hermes's gateway drains for 30 seconds with a request in flight, past core's 10-second stop limit, so api.token stays. (finding 7) |
stop removes files first, then SIGTERM, 5 seconds, SIGKILL; adapters set the harness's own drain below 5 seconds. |
L5 |
| Q5 | The health check passes against whatever already holds the port. (finding 5) | Check the port is free before launch; after health, confirm the child runs and owns the listening socket. | S2, L2 |
| Q6 | OpenClaw accepts any loopback origin even with an explicit allowedOrigins list. (finding 1) |
Accepted for P0 and documented; public_origins is for non-loopback origins. Exact-origin enforcement is to be raised with OpenClaw upstream. |
S5 |
| Q7 | OpenClaw's browser relay binds base+10, outside the documented reservation. (finding 2) | Reserve the whole range from base to base+110; start checks only base and base+2. |
S2, #541 |
| Q8 | Hermes's dashboard gateway actions start detached processes that replace the Fabric-started gateway and outlive stop. (finding 6) |
Documented as a P0 limitation. | A4, L7 |
| Q9 | Hermes's API-server child didn't use FabricConfig's endpoint and key. (finding 8) |
Every Hermes child that calls a model uses them, set in that child's configuration only, and a test against a local endpoint checks the requests' URL and key. | C1, #540 |
Follow-up: exact-origin enforcement is requested upstream in openclaw/openclaw#190113 (Q6).
Earlier questions, answered for P0: credentials are regenerated at each start (T1), retained ones wait for state_dir (#566); interfaces are runtime-bound (L1); terminal interfaces are launched by path (A3) until #565 reports a command in the handle; conversations aren't promised as shared (C2).
State chart of the P0 lifecycle, before Q1 and Q2 were settled
---
title: "P0 interface lifecycle, scratch model of NeMo-Fabric issue 547"
---
stateDiagram-v2
state "Legend" as legend {
lg0: live
lg1: ended
lg2: Breaks MarkerOnlyWhileRunning or NoChildOutlivesItsRuntime or TokenOnlyWhileServing
lg3: Path to the MarkerOnlyWhileRunning or NoChildOutlivesItsRuntime or TokenOnlyWhileServing bug
}
class lg0 teal
class lg1 tan
class lg2 breaks
class lg3 bugPath
s0: Not started
s1: Starting
s2: Adapter killed (token file left)
s3: Start failed (token file left)
s4: Adapter killed (children left running, token file left)
s5: Running
s6: Adapter killed (children left running, token file left, marker left)
s7: Stopped
s8: Adapter killed (token file left, marker left)
[*] --> s0
s0 --> s1: start writes the token
s1 --> s2: the adapter is killed
s1 --> s3: a child exits or times out
s1 --> s1: a child launches or passes health
s1 --> s4: the adapter is killed
s4 --> s2: a leftover child exits
s1 --> s5: every child passes health
s5 --> s6: the adapter is killed
s5 --> s7: stop
s6 --> s8: a leftover child exits
s5 --> s8: the adapter is killed
classDef breaks fill:#ffecee,stroke:#e72e3b,stroke-width:2.5px,color:#cd2834
classDef teal fill:#bfe3df,color:#000
classDef tan fill:#e5d9ce,color:#000
classDef bugPath stroke:#1680f3,stroke-width:2.5px
class s0 teal
class s0 bugPath
class s1 teal
class s1 bugPath
class s2 tan
class s2 breaks
class s3 tan
class s3 breaks
class s3 bugPath
class s4 tan
class s4 breaks
class s4 bugPath
class s5 teal
class s5 bugPath
class s6 tan
class s6 breaks
class s6 bugPath
class s7 tan
class s8 tan
class s8 breaks
The model and TLC configuration are in this comment. It models the text before the answers, not code: Q1 removes the failed-start path, and Q2 accepts the paths through "Adapter killed" for P0.
Pinned versions
| Component | P0 targets | Fabric main (624e687) |
NemoClaw v1 image (Fabric 7aedb66) |
Review ran against |
|---|---|---|---|---|
| OpenClaw | 2026.9.34 | 2026.9.34 | 2026.9.4 | Both; findings 1 and 2 hold on both |
| Hermes | v0.22.5, the latest release (bf0203a) |
v0.22.5 | v0.22.4 (4d04303) |
v0.22.4 |
| Deep Agents Code | Set by #546 | Not pinned | Not packaged | 0.2.2 |
| OpenShell | – | – | 0.1.6 (ac7f615c) |
– |
A4 and the Hermes findings were established on v0.22.4, while Fabric main tests v0.22.5, 261 commits later. A spot check of v0.22.5 found the same agent.cron_drain_timeout (default 30) and agent.restart_drain_timeout settings, the POST /api/gateway/restart route, the browser-chat flag and APIServerAdapter; #540's reference implementation confirmed A4 on v0.22.5, where the loopback dashboard still embeds its session token.
Target contract (after P0)
The full contract the P0 adapters start from. #565 delivers discovery and shared code; #566 tracks the rest. Where this list differs from the P0 specification, the P0 specification wins for P0.
- Settings. Each interface is opt-in under
harness.settings.interfaces.<name>(S1 to S9). Browser interfaces addpublic_origins, which the adapter maps to the harness's own setting, such as Hermes'sdashboard.public_url, OpenClaw'sallowedOriginsor OpenCode's--cors. An adapter rejects an interface it doesn't support, or one its lifecycle shape can't serve, during planning. - Discovery ([Enhancement]: Interfaces: generalize discovery and shared adapter code from the P0 adapters #565). The descriptor declares the interfaces an adapter supports, with each one's kind (HTTP, terminal or stdio). The service or runtime handle reports each enabled interface's loopback port and path, and where its credential is, never the credential itself, so a consumer can forward any adapter's interfaces without adapter-specific code.
- Network. Interfaces listen on loopback only, and exposing them is the consumer's job. Each interface declares its authentication scheme (Bearer, Basic, custom header, cookie or verifier) and which exposure paths it works through ([Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566).
- Credentials. Two modes, declared per interface. Adapter-generated (P0: T1 to T3). Consumer-supplied verifier ([Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566): the consumer keeps the raw credential outside the agent's environment, and the harness, or a verifying proxy in the adapter, receives only its hash. Each adapter documents the interface features that would let an agent bypass its guardrails, and disables them in the adapter-generated mode ([Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566).
- State ([Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566).
state_dirkeeps the harness's state and the credential across runtimes and services, under a stable consumer-chosen key, with one owner at a time and documented stale-lock recovery, resume or a documented lack of it, documented concurrent writes, the harness version that wrote it, and cleanup that never deletes it. - Configuration ownership. Generated from
FabricConfigat each start (C1). A terminal command reported in a handle includes the adapter's configuration flags and environment. Fabric-owned settings (model and endpoint, keys, instructions, tools, MCP servers, skills, approval mode, telemetry, workspace) can't be changed through an interface; session choices stay within whatFabricConfigdeclares; personal preferences are retained instate_dir. The handle orreadycheck reports a configuration digest and drift ([Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566). - Lifecycle. The descriptor declares whether each interface lives with a runtime or a service (P0: runtime, L1). Startup failure, fixed ports, failure and stop as L2 to L6;
optionalinterfaces, service binding, terminal clients honoring a state lock and exiting when the runtime stops, and serializing interface turns with Fabric invocations or reporting them in activity are [Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566. - Health. P0 keeps separate interface processes out of the runtime check (L4). An
interfacecheck target withlive,activeandreadylevels, declared in the descriptor, is [Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566. - Conversations. Each adapter documents whether interface conversations share threads with Fabric invocations (C2); a shared default is [Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566.
- Outbound requests. As C3.
- Packaging. As C4, and the descriptor lists the binaries it runs.
Implementation strategy
| Step | Work | Depends on | Issue |
|---|---|---|---|
| A | Agree the P0 specification and the scope changes to #540, #541 and #546 (done, including Q1 to Q9) | – | #547 |
| B | OpenClaw Control UI | A | #541 |
| C | Hermes, runtime-bound API server and dashboard children | A | #540 |
| D | Deep Agents Code: investigate terminal attachment first, then the adapter | A | #546 |
| E | Consumer integration and qualification, for example NemoClaw's images, settings, forwarding, token instructions and live tests | B, C, D | NVIDIA/NemoClaw#14011 |
| F | Generalize from B, C and D: descriptor interface names and kinds, the shared settings fragment, interfaces in RuntimeHandle, a read-only status command (#420), shared Python code |
B, C, D | #565 |
| G | Deferred contract items and the negotiable adapters (#550 to #560) | F | #566 |
B, C and D run in parallel. GTC needs A to E.
Public contract and compatibility impact
- Rust API: none expected, unless handle discovery needs typed fields.
- Python API: none; consumers use the existing service and runtime calls.
- Schemas or adapter contract: descriptor fields for supported interfaces, handle fields for enabled ones, and each adapter's
interfacessettings. - Harness or consumer integrations: a consumer reads interface ports and credential locations from handles instead of adapter-specific knowledge.
- Backward compatibility: additive; every interface defaults to off.
Alternatives considered
- Each adapter on its own. It works, but consumers would integrate a different shape for each of the fourteen sub-issues, and later adapters would add more.
- A generic native-configuration passthrough. It lets consumers turn interfaces on themselves, but it widens the contract and lets their configuration conflict with Fabric-owned settings (see [Enhancement]: OpenClaw adapter: browser dashboard and chat #541).
- Consumer-side supervision. Consumers would duplicate configuration, credential, state and lifecycle handling outside Fabric and drift from it.
Acceptance criteria
- P0: [Enhancement]: OpenClaw adapter: browser dashboard and chat #541, [Enhancement]: Hermes adapter: API server, browser dashboard and chat #540 and [Enhancement]: Deep Agents Code adapter: new adapter with terminal UI #546 meet their acceptance criteria under the P0 specification, and Offer the P0 agents' native interfaces: OpenClaw's Control UI, Hermes's dashboard and API, and Deep Agents Code's terminal NemoClaw#14011 reaches each of their interfaces by convention.
- After P0 ([Enhancement]: Interfaces: generalize discovery and shared adapter code from the P0 adapters #565): handles report interfaces, the shared settings fragment and Python common code exist, and a consumer test locates each P0 interface through the handle and stored credential, with per-adapter sign-in.
- Later ([Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566 and the negotiable sub-issues): each implements its interfaces on the same contract.
Additional context
NemoClaw's counterparts: NVIDIA/NemoClaw#13969 (Hermes), NVIDIA/NemoClaw#13360 (OpenClaw's dashboard) and NVIDIA/NemoClaw#13973 (signing in to it). NemoClaw forwards loopback services through OpenShell and doesn't patch adapter-owned configuration (NVIDIA/NemoClaw#13318).
Background research
The research behind the contract, moved here from comments. It records what was found and proposed at the time; where it differs from the P0 specification, the specification wins.
Implementation notes from Fabric `main` at `4fdf65a`
Implementation notes from Fabric main at 4fdf65a: nothing named interfaces exists yet, but these pieces can carry the shared contract.
- Settings validation: each descriptor's
settings_schemais a JSON Schema forharness.settings.fabric-corevalidatesharness.settingsagainst it during planning (config.rs) and reports errors at the setting's path, such asharness.settings.port_range. Aninterfacesproperty in a descriptor is validated with no new core code. - Closest existing settings: OpenClaw's
port_range(startandend, spanning at least 111 ports),agent_id, its timeouts, andchannel_config, in a schema that rejects unknown properties. - Declaring interfaces:
capabilitieshasserviceand a free-formmetadataobject, which OpenClaw already uses forhealth_checksandservice_health_checks. Supported interfaces could be declared there or in a new typed field. - Reporting interfaces:
ServiceHandlehasconnection, a "sanitized connection summary" that "never contains credential values", andmetadata. Each enabled interface's loopback port, path and credential location fits there. - Gap: no shared schema fragments. Each descriptor is self-contained, with no shared
$defsor$ref. Shared settings are copied into each descriptor and have drifted:insecure_http_model_hostsaccepts IPv6 literals in Codex's descriptor but only hostnames in Kilo's, while their runtime handling lives in common code (model-endpoint.ts,relay_http.py). Theinterfacesschema should be defined once, for example as a fragment in the adapter contract that a build step or check keeps identical in every descriptor, rather than copied into fourteen.
Discovery design (field names are proposals; #565 settles them)
Design proposal for discovery. The field names are proposals, not decisions.
Two discovery questions
-
Before deployment: which interfaces does an adapter support? Fabric resolves descriptors from
*.fabric-adapter.jsonfiles and its bundled catalog. NVIDIA NemoClaw copies installed adapters' complete descriptors into its image label, and its compatibility check treats an absent claim as unknown, not supported. A typedinterfacesdeclaration in descriptors would reach consumers' planning with no consumer changes. -
After start: where is an enabled interface, and how does a person sign in? Today:
ServiceHandlehasconnectionandmetadata, but services exist only through the SDK'sprepare_service()andattach_service().fabric-agent-ctl, which sandboxed consumers use, offersserve,check,start,invokeandstop: no service operations, and no way to read a runtime's handle afterstart.RuntimeHandlecarries identity, harness, adapter,service_idand environment, but no connection details.
So a consumer that drives agents through
fabric-agent-ctlcan't reach a service-shaped interface such as [Enhancement]: Hermes adapter: API server, browser dashboard and chat #540 proposes, or rediscover an interface afterstart. This answers the open question on managed runtimes: either runtimes may enable interfaces, orfabric-agent-ctlgains service operations. Either way,RuntimeHandleneeds to report interfaces, andfabric-agent-ctlneeds a read-only command that returns the current handle (fabric-agent-ctl: read-only status command for the owned runtime #420).
Proposed shape
Descriptor: what an adapter supports. A typed top-level field rather than free-form capabilities.metadata, so its absence is meaningful:
"interfaces": {
"dashboard": { "kind": "http", "browser": true, "credential": "bearer", "lifecycles": ["service", "runtime"], "shares_conversations": false },
"terminal": { "kind": "terminal", "lifecycles": ["runtime"], "shares_conversations": true }
}Settings: what a consumer enables, using the one shared interfaces schema fragment from the comment above, validated through settings_schema:
harness:
settings:
interfaces:
dashboard: { enabled: true, port: 19119, allowed_origins: ["https://example.test"] }Handles: what is running. RuntimeHandle and ServiceHandle report each enabled interface, without secrets:
"interfaces": {
"dashboard": { "kind": "http", "address": "127.0.0.1", "port": 19119, "path": "/",
"credential_file": "/sandbox/artifacts/.fabric/hermes/runtimes/RUNTIME_ID/interfaces/dashboard.token" },
"terminal": { "kind": "terminal", "argv": ["kilo", "attach", "http://127.0.0.1:4096"],
"credential_env_files": { "KILO_SERVER_PASSWORD": "/sandbox/artifacts/.fabric/kilo/runtimes/RUNTIME_ID/interfaces/terminal.token" } }
}A terminal interface reports the command to run in the agent's environment, which answers the open question on terminal interfaces without a consumer knowing each harness's CLI.
Consumer flow
For NemoClaw:
- YAML sets
harness.settings.interfaces. - Planning checks it against the image's descriptor.
fabric-agent-ctl startreturns the handle withinterfaces.- NemoClaw keeps the non-secret fields.
- It tells the person how to forward each port with OpenShell and how to read the credential file.
- A read-only
fabric-agent-ctlcommand returns the current handle on later reads.
Network and credentials
Network and credentials, from OpenShell's documentation and CLI at ac7f615c (the revision NVIDIA NemoClaw pins) and the harnesses' own authentication code.
Two ways to reach an interface
openshell forward service SANDBOX --target-port P --local PORT |
openshell service expose SANDBOX PORT [NAME] |
|
|---|---|---|
| Transport | TCP over the gateway; WebSockets and any authentication scheme work | HTTP, at a gateway-managed URL |
| Authorization | Each connection against the caller's gateway identity and workspace | "Service routes bypass control-plane RPC authorization"; the application's own authentication is the only check |
| Host the browser sends | 127.0.0.1:PORT, which loopback Host checks accept |
<sandbox>--<name>.openshell.localhost:<port> on a loopback gateway, or HTTPS on a remote one |
Authorization header |
Unchanged | Stripped by default; --authorization-mode bearer-passthrough forwards one Bearer credential; Basic fails with 400 |
Consequences:
- Origins. Hermes's dashboard accepts only loopback Hosts or its
dashboard.public_url, against DNS rebinding; OpenClaw hasallowedOrigins; OpenCode has--cors. Every browser interface needs a public-origin setting for exposed URLs, not just OpenClaw's ([Enhancement]: OpenClaw adapter: browser dashboard and chat #541). - Schemes. Kilo and OpenCode servers use HTTP Basic (
KILO_SERVER_PASSWORD,OPENCODE_SERVER_PASSWORD), which exposed URLs reject; OpenHands's agent server uses anX-Session-API-Keyheader, which browsers can't add. Those interfaces work throughforward serviceonly, unless the application signs browsers in itself. - The agent can reach its interfaces. OpenShell's network policy doesn't cover sandbox-local loopback (
policies/network-rules.mdx: "a workload can reach a service listening on127.0.0.1").
Who a credential stops
Everything in a sandbox runs as one identity (run_as_user):
| Who | Path | Credential |
|---|---|---|
The person, through forward service |
Gateway-authorized | Defense in depth |
| Anyone who reaches an exposed URL | Not gateway-authorized | The only protection |
| Other processes on a loopback gateway's host | Local service URL | Needed |
| The agent's own tools | Same sandbox and identity: the loopback port and owner-only files (but not another process's environment; see the correction below) | Doesn't stop a file-based credential |
So with an adapter-generated token in a 0600 file, a prompt-injected agent can sign in to its own interface and, depending on the harness, approve its own tool calls, change its model or tools, or edit cron jobs and plugins: it escapes the harness's guardrails, though not OpenShell's policy.
OpenShell documents the mitigation: keep the raw token outside the sandbox and give the harness only its SHA-256 verifier. Codex's app-server supports it (--ws-auth capability-token --ws-token-sha256). Hermes, OpenClaw, OpenCode, Kilo, Qwen and OpenHands take a raw token or password; Deep Agents Code's agent server has no authentication.
Correction, after testing. In NVIDIA NemoClaw's Hermes sandbox, openshell sandbox exec runs as the harness's identity (uid 10001), but the harness processes run with no-new-privileges, a seccomp filter and Yama ptrace_scope 1: the /proc/<pid>/environ of fabric-agent-ctl serve, the Hermes adapter, and even a plain process started from another exec weren't readable. Owner-only files of that identity are readable. So a credential held only in a harness process's environment isn't readable by other processes, though the harness's own children inherit it unless it scrubs them; a credential in a file is readable by the agent's tools.
Proposed additions to the contract
- Credential modes. Adapter-generated, the current proposal, which the agent can use; and consumer-supplied verifier, where the consumer keeps the raw token outside the sandbox and the harness, or a small verifying proxy in the adapter, receives only its hash. Each descriptor declares the modes it supports, and the documentation states that the first doesn't protect against the agent.
- Declared scheme and exposure compatibility. Each interface declares its authentication scheme (Bearer, Basic, custom header, cookie or verifier) and which exposure paths it works through, so a consumer can choose
forward serviceor an exposed URL without knowing each harness. - One
public_originssetting for browser interfaces, which the adapter maps to Hermes'sdashboard.public_url, OpenClaw'sallowedOriginsor OpenCode's--cors. - Guardrail-sensitive features. Each adapter documents which interface features would let an agent bypass its guardrails, such as approvals, configuration edits or plugin installation, and disables them in the adapter-generated mode.
State and configuration ownership
State and configuration ownership, from NVIDIA NemoClaw's state documentation (which lists every pinned adapter's state locations) and the adapters' READMEs on Fabric main.
State today
| Adapter | Native state | After a runtime restart |
|---|---|---|
| Hermes | Home and session database under .fabric/hermes/runtimes/<runtime-id> |
A new directory; empty memory |
| Deep Agents | SQLite checkpoints per runtime key | A new thread ID; the old file doesn't resume |
| OpenClaw | A private temporary directory | Removed on stop |
| Pi, OpenCode, mini-SWE-agent | In memory | Lost |
| Claude, Codex | Generated files under .fabric/<adapter>; native login and history not inventoried |
A new client or thread |
No adapter reattaches a new runtime to saved native state, and consumers such as NemoClaw replace the sandbox on any configuration change. Interface state (sessions started in the interface, preferences, sign-ins made through it, and its credential) would therefore disappear on every restart, and mounting a volume doesn't help while directories are keyed by runtime ID.
Proposed for state_dir
- Stable key. Named by the consumer, for example by deployment and agent, rather than by runtime ID, so restarts and replaced sandboxes find it again; consumers put it on a volume when it must outlive the sandbox.
- Resume. A new runtime continues the retained sessions, or the adapter documents that it doesn't; otherwise an interface starts empty after every restart.
- One owner, with recovery. An exclusive lock, and a documented way to recover a stale lock after a crash or a killed sandbox.
- Concurrent writers. A Fabric invocation and a person may write the same session store at once. Server-shaped harnesses (OpenClaw's Gateway,
kilo serve) arbitrate this; an in-process SDK plus a separate terminal process may not, for example two processes on Hermes's SQLite database or Pi's in-memory session. Each adapter documents its behavior. - Layout. Generated configuration (regenerated each start), sessions (retained), the interface credential (retained or rotated, per the credential-lifetime question), personal preferences (retained) and logs.
- Versions. The adapter records the harness version that wrote the state and refuses or migrates it explicitly when Fabric's harness pin moves.
- Cleanup and sensitivity. Adapter cleanup never deletes a consumer-provided
state_dir. Its contents include credentials and sign-ins and are as sensitive as a home directory.
Configuration ownership
Adapters already generate native configuration and disable ambient configuration: Claude's setting_sources defaults to []; OpenHands, OpenCode and Pi disable user and project configuration; Qwen uses an isolated configuration; Droid doesn't copy ambient settings; OpenClaw generates openclaw.json; Codex uses a private CODEX_HOME. Interfaces add three problems:
- Separate processes read ambient configuration. A terminal UI started by a person (
pi,kiro-cli,cline -i,dcode) loads the user's configuration unless it's pointed at the adapter's, and can then run with a different model, tools or MCP servers. The terminal command a handle reports must include the adapter's configuration flags and environment, not only the executable. - Interface edits diverge. Interfaces can switch models, add MCP servers, install plugins or edit skills. Those changes bypass the consumer's declared configuration, Fabric's usage accounting and Relay tracing, and are silently lost at the next start.
- Classes of settings:
| Class | Examples | Rule |
|---|---|---|
| Fabric-owned | Model and endpoint, keys, instructions, tools, MCP servers, skills, approval mode, telemetry, workspace | Not changeable through an interface; the adapter locks or hides them where the harness allows, and regenerates them each start |
| Session choices | Switching between declared models, reasoning effort | Allowed only within what FabricConfig declares |
| Personal preferences | Theme, key bindings, layout | Allowed, and retained in state_dir |
Drift. The handle or the ready check reports a digest of the generated configuration and whether the live configuration still matches it, so a consumer can warn that an agent no longer matches its declared configuration.
Lifecycle and health
Lifecycle and health, from Fabric's health-check guide, OpenClaw's service documentation and the runtime server README on main, and NVIDIA NemoClaw's Fabric provider.
Today
- Managed runtimes own their harness processes; OpenClaw's managed Gateway stops with its runtime.
prepare_service()services are Fabric-owned;attach_service()services are caller-owned and never stopped by Fabric; release is rejected while runtimes are connected; resuming after the Fabric process exits isn't supported yet. fabric-agent-ctl serve:stopdoesn't establish that descendants exited, the sandbox supervisor cleans up,fabric.uncleanrecords unconfirmed cleanup, and SIGTERM during work reportsruntime_busy. Fabric doesn't restart a failed harness.- Health:
server,runtimeandservicescopes (serviceonly through the local SDK, notfabric-agent-ctl); cumulativelive,activeandreadylevels with adapter-specific evidence;passed,failed,unknownandunsupported; one three-second deadline; activity reported separately and counting only Fabric invocations. Droid, Kiro and Strands lackactiveandready; mini-SWE-agent, NOOA and remote-agent advertise no levels. - NemoClaw's provider runs
fabric-agent-ctl check --agent --runtime-id;failedmeans not running, so OpenTofu plans to replace the sandbox, whileunknownandunsupportedcount as running.
Lifecycle proposals
- Binding. The descriptor declares whether each interface lives with a runtime (Kilo's server, terminal attach) or a service (OpenClaw's Gateway, [Enhancement]: Hermes adapter: API server, browser dashboard and chat #540's Hermes service). Consumers limited to
fabric-agent-ctlcan use runtime-bound interfaces until it gains service operations. - Start. An explicitly enabled interface that can't start, for example because its port is taken, fails
startwith a stable error, unless the settings mark it optional. - Ports. A consumer-chosen port stays fixed across restarts, so forwards keep working; the adapter fails rather than choosing another. Unchosen ports are adapter-selected and reported in the handle.
- Failure. When the interface is the agent's own server (OpenClaw's Gateway,
kilo serve, Deep Agents Code's agent server, a Hermes service), its failure is the runtime's or service's failure. A separate interface process, such as feat(hermes): run Hermes as a native API server with retained state #327's dashboard child, isn't restarted, as elsewhere in Fabric: its health fails and the runtime keeps running. - Stop and release. Stopping a runtime or releasing a service closes its interfaces. Terminal clients started by a person, for example through
openshell sandbox exec --tty, aren't descendants offabric-agent-ctl serveand can outlive the runtime, and a client with its own session store could keep writing state. Terminal clients should honor thestate_dirlock: attach while a runtime holds it, never open the store independently, and exit when the runtime stops. - Concurrent turns. A person in an interface and a Fabric invocation can address one session at once; activity doesn't count interface turns, and the health guide notes that activity "does not establish that an agent will accept a second concurrent prompt". Each adapter serializes the two or reports interface turns in activity, and documents which.
Health proposals
- An
interfacetarget, for examplefabric-agent-ctl check --agent A --runtime-id R --interface dashboard --ready, so an interface failure doesn't fail the runtime check; otherwise a broken dashboard would make NemoClaw replace the sandbox. The exception is an interface that is the agent's own server, which the runtime check already covers. - Levels:
live: the process runs and its port is bound.active: its unauthenticated health endpoint answers (Hermes/healthand/api/health, OpenClaw/healthz, LangGraph/ok).ready: an authenticated request with the stored credential succeeds and the configuration digest still matches. In the consumer-supplied verifier mode the adapter has no raw credential, soreadycan only confirm that authentication is required. - Boundary. Fabric checks loopback inside the agent's environment; the exposure path (an OpenShell forward or service URL) is the consumer's to check, and person-started terminal clients aren't Fabric's.
- Advertising. The descriptor lists each interface's supported levels beside
health_checksandservice_health_checks.
Change log
- 2026-10-29: epic opened; implementation notes, discovery, network and credentials, state, and lifecycle research posted as comments (now under Background research).
- 2026-10-30: P0 decisions narrowed the scope: runtime-bound interfaces, adapter-generated tokens at each start, discovery by convention; [Enhancement]: Interfaces: generalize discovery and shared adapter code from the P0 adapters #565 and [Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566 split out; [Enhancement]: Hermes adapter: API server, browser dashboard and chat #540 made runtime-bound.
- 2026-10-30: two specification reviews added the single settings rule (S1 to S9), the artifact root, startup failure and timeout (L2, L3), token cleanup on
stop(L5),sandbox exec --ttyfor terminals (A3), and Hermes's dashboard without sign-in (A4);optionalremoved from P0. - 2026-10-30: an adversarial review and a lifecycle model raised Q1 to Q9.
- 2026-10-30: decisions, clarifications and research consolidated into this description and the sub-issues' descriptions; the comments they replace are hidden as outdated.
- 2026-10-30: the epic's author settled Q1 to Q9; S2, S5, L2, L3, L5, P4, T1, A4 and C1 changed, and L7 was added.
- 2026-10-30: building [Enhancement]: OpenClaw adapter: browser dashboard and chat #541's reference implementation settled two gaps: S2's ownership check is Linux-only, and L3's maximum applies to existing startup timeouts such as OpenClaw's (default 30).
- 2026-10-30: building [Enhancement]: Hermes adapter: API server, browser dashboard and chat #540's reference implementation settled how Q9 works, confirmed A4 on Hermes v0.22.5, and recorded Hermes's packaging needs; [Enhancement]: Hermes adapter: API server, browser dashboard and chat #540 lists them.
- 2026-10-30: [Enhancement]: Deep Agents Code adapter: new adapter with terminal UI #546's investigation found that
dcodecan't attach to another process's agent; the epic's author chose onedcode --acpprocess per runtime, with the terminal UI as a separate session on the shared store. [Enhancement]: Deep Agents Code adapter: new adapter with terminal UI #546 records the outcome. - 2026-10-31: NemoClaw's live Kubernetes tests showed OpenClaw's Gateway taking about 40 s to start when three agents start at once on four vCPUs, against the adapter's 30 s per-step timeout. OpenClaw's
startup_timeout_secondsis now one deadline for all ofstart, defaulting to 60, as L3 says (reference implementationa7c5d024). [Bug]: Deep Agents Code's terminal UI can't resume the runtime's thread #567 recorded that Deep Agents Code's terminal can list but not open the runtime's thread; [Enhancement]: Deep Agents Code adapter: new adapter with terminal UI #546 and the docs now say so. - 2026-11-02: closed; the GTC DC scope moved to [Enhancement]: Epic: GTC DC P0 for NemoClaw: OpenClaw and Hermes as stock harnesses #577, and the deferred sub-issues to [Enhancement]: Epic: deferred until after GTC DC: interface contract items and other harnesses #566.
Duplicate check
- I searched the existing issues and did not find a duplicate.
Activity
22 remaining items
jyaunches commentedon Nov 2, 2026
Closing in favor of #577, agreed with the Fabric team on 2026-11-02. #577 tracks the GTC DC scope: OpenClaw, Hermes and Deep Agents Code, with cold resume first (#578), terminal UIs (#579) and skills on mounted volumes (#580). The P0 specification in this description still applies, extended by #578 and #579. #565 and #550 to #560 moved under #566, which tracks everything deferred until after GTC DC.