Appearance
The host
A host is what a client connects to: one root resource, the sessions running under it, the automations it holds and the resources it serves. One ahpd process runs one host, reachable over a WebSocket, over stdin and stdout inside a container, or over an HTTP API.
The process, its verbs and its flags are REFERENCE.md. This page is the host itself: what it announces, what it serves, and every setting it reads.
Terms, one line each:
| Term | |
|---|---|
| the host | the thing a connection is accepted by, and the name for everything below it |
| the root resource | the channel ahp-root://, which every client subscribes to |
| root state | the snapshot that channel answers: agents, activeSessions, terminals and config |
| root config | the keys a client pushes to ahp-root:// with root/configChanged |
| a scheme | the part of a resource URI before the colon - file, user, usage, or a plugin's |
| a grant | <subject>:<operation>, one act on one kind of thing - USERS.md |
The root resource
ahp-root:// is the channel a client subscribes to before it knows anything else. Its state says what this host is:
| Field | What it says |
|---|---|
agents | Every backend, as providers - see below |
activeSessions | A number: how many sessions are running a turn, not a list |
terminals | Every open terminal's TerminalInfo, absent when none is open |
config | The schema and the values of root config - see Root config |
_meta carries two things beside them: ahpd.resourceProviders, the schemes this host serves; and ahpd.grants, every subject a grant may name. ahpd.principal is there for a connection that has already signed in, saying who it is - the same statement the handshake makes, so a client that subscribes later reads what a client that connected earlier was told.
The state of the root, of every session and of every chat is what the protocol's reducers make of the actions this host sends on that channel, from a seed read once when the channel opens. A client that subscribes is served that state, so it holds what a client that watched every action holds.
ahpd.resourceProviders is advertised in the handshake as well as in every root state, because a client needs it before it can ask for anything.
The root channel is also where a client's own reads are served, whatever they are about: listSessions, the resource calls, authenticate, createResourceWatch and resolveSessionConfig all declare ahp-root:// as their channel. The four root actions and their origins are AHP.md.
What a host announces
Every entry of agents is one backend, as the protocol's AgentInfo:
| Field | What it says |
|---|---|
provider | The id a client names in createSession, and the scheme its session URIs use |
displayName, description | What a person reads instead of the id |
models | What the backend's probe found, each row carrying its own provider because the wire type requires one. A backend that has not answered yet announces none, which is the honest form of "nobody has signed in" |
protectedResources | The resources a client may send authenticate a token for. Absent when there are none, which means no credential can be handed to this backend at all |
customizations | The skills, subagents and MCP servers a session of this backend starts with, so a client can show what a harness offers before any session exists |
capabilities | multipleChats, with fork and sideChat where the backend can resume at a turn; and multipleWorkingDirectories, with immutablePrimary and primaryReplacement where it takes more than one directory |
A host can be told which agents are off, as HostOptions.disabledAgents: the ids whose record says mode: disabled, read each time the list is asked for. A disabled agent is left out of agents, and refused a new session, a new chat, a chat move and a new turn with <id> is turned off. Its sessions keep their history and stay listed, and take a message again once the agent is on.
HostOptions.pausedAgents is the same for an agent whose record says mode: paused: its backend is served and opened, so its sessions keep their history and its alerts show, and it is left out of agents and refused a new session, a new chat, a chat move and a new turn with <id> is paused. A wake of a stored session reads its history, because the record is opened; a message to it is refused.
ahpd.grants is the other half of what a client is told: one entry per subject a grant may name, each with its title, a one-line description, its operations and the two groups those fall into. Eleven subjects are the host's own - session, chat, file, automation, terminal, diagnostics, container, config, trust, proxy and account, plus the people and cost schemes - and every scheme a provider serves is beside them under its own name. A client draws a role editor from this one map, which is why it is advertised with or without a user directory: it says what a role could hold, not what anybody holds.
What a host serves
file: is the host's own scheme. Every other scheme in ahpd.resourceProviders is a provider's: user:, team:, project: and role: when a directory is configured, policy: and usage: always, and whatever a plugin registered. Each entry carries the provider's own describe(), the root URI - <scheme>:// - and the operations its methods implement, read off the provider rather than off a list somebody maintains.
A scheme is advertised so a client can put its operations in a role and have the gate ask for what it will actually call. What each scheme is and who may read it is RESOURCES.md.
Configuration keys
Every flag can be a key in the configuration file instead, spelled without the dashes. How the files are found, merged and checked is CONFIGURE.md; this is what the keys are.
| Key | Values | Default | What changes |
|---|---|---|---|
port | integer | 9187; 0 picks a free one | The port the host listens on |
host | address | 127.0.0.1 | The address it binds. 0.0.0.0 accepts from other machines, and then a token is required |
origins | list of origins, such as http://203.0.113.5:9187 | none | Extra origins a browser may reach this host by. A host bound to 0.0.0.0 answers only to its loopback names, so a page served on its public address needs that address here |
paths | list of directories | the directory the daemon was started in | The directories this host serves |
worktreesRoot | directory | <repo>.worktrees beside each repository | Where session worktrees are kept |
clientToolTimeoutMs | integer, milliseconds | 600000 (ten minutes) | How long a tool call a client runs may wait for that client. 0 waits for ever |
clientToolCallDisconnectTimeoutMs | integer, milliseconds | 30000 (thirty seconds) | How long a client whose connections all closed keeps its place in a session and the tool calls it owes there, VS Code's CLIENT_TOOL_CALL_DISCONNECT_TIMEOUT; when it runs out, each call it owes fails in VS Code's words. 0 releases it at once |
shellEnv | an object of variable name to text or null | none | The environment every shell this host starts is given. A client that needs a variable in one session sets shellInitScripts instead |
terminalAutoApproveDefaults | vscode or none | vscode | The rules a session's shell commands are matched against when no window pushed any. vscode is VS Code's table, none approves no command - AHP.md |
managedSettingsPermissions | VS Code's managed permissions: deny and ask rule lists, disableBypassPermissionsMode: 'disable', limitTo | none | The operator's managed rules: under managedRules: 'checked' the host checks each on the tool questions of its kind; the lock takes Allow All away, and every backend is handed them on Start.managedSettings, under either value - PLUGINS.md |
managedSettingsSandbox | VS Code's floor shape, SessionSandboxPolicy | none | The floor under every session's sandbox: a chat's sandbox is resolved within it, it is published on each session as vscode.resolvedSandboxPolicy, and one with failClosed keeps the sandbox on with no way out - COMPUTERS.md |
eligibleForAutoApproval | VS Code's map of tool name to boolean | none | The operator's per-tool eligibility: under managedRules: 'checked' a tool set to false asks on every call with Allow Once and Skip alone, read on every tool question; it is handed to every backend on Start.managedSettings beside the managed rules under either value - PLUGINS.md |
managedRules | checked or handed | checked | Who checks the two above on a tool question: checked is this host's own check of each managed family on every question of its kind from every backend; handed is VS Code's, the host checking no managed rule and marking nothing, handing the rules to the backends and putting a question a backend marked managedApprovalRequired with Allow Once and Skip. The start's check of the managed document and the Allow All lock hold under both |
commandParser | file path | unset | A .js or .ts module whose default export splits a shell command before the terminal rules match it. A path is relative to the configuration file - PLUGINS.md |
deltaWindowMs | integer, 0 to 1000 | 75 | How long a turn's streamed text is gathered before it is sent. 0 sends every delta as it arrives |
sleepAfterMinutes | integer, minutes | 30; 0 never | How long a session with nothing in hand stays loaded before its agent is let go. It wakes when something needs it - SESSIONS.md |
sessionResidencyLimit | integer | 10; 0 sets no limit | How many sessions are resident at most, running or with their state held. Over it, the least recently used one that nothing holds is let go first, with its state - SESSIONS.md |
attachmentFetchLimitBytes | integer, bytes | 33554432 (32 MiB); 0 sets no limit | The largest file a client is asked for when a message attaches one this host has no copy of; one whose size hint is over it is left as it was sent - CHATS.md |
toolResultResourceLimitBytes | integer, bytes | 8388608 (8 MiB, VS Code's MAX_TOOL_RESULT_RESOURCE_BYTES); 0 sets no limit | The most bytes kept of one content a backend hands Start.storeToolResultResource; larger content is kept nowhere and the backend leaves it out - RESOURCES.md |
advancedTools | boolean | false | Offer the tools that declare advancedPermission to every session's model |
sessionTools | boolean | true | Whether the session tools - the nine that read and move the sessions beside this one - are offered at all. false offers them to no session. The artifact tools and schedule_wakeup are not session tools and are offered either way |
sessionToolsProviders | list of provider ids | none | Offer the session tools to the sessions of these providers alone. Empty, the default, offers them to every provider. It narrows sessionTools rather than replacing it, so with sessionTools false no session is offered them whatever this list holds |
closedChats | hidden or delete | hidden | What closing one chat of a session does to that chat's conversation in the backend: hidden keeps it, delete removes it |
unofferedModel | fail or keep | fail | What a turn naming a model its chat's agent does not offer does: fail ends it in a modelSelectionFailed error, keep is VS Code's, a chat whose agent is not its session's keeping its model and any other chat's agent handed the model as it is |
workspacelessFolder | first or scratch | first | Where a session that names no folder works: first names it none, so it works in the first folder this host serves; scratch is VS Code's, a folder of its own at <home>/.copilot/chats/<session id>, made when the session is, made again when a start finds it gone and removed with the session. A session id that names no folder under the chats folder is refused under scratch. A session working in its scratch folder is VS Code's workspace-less session: its row and state carry _meta.workspaceless: true, its agent is told last, in VS Code's words, that it works in a scratch folder, set_workspace is offered to it up front, a chat of it is refused a move, its folders are refused a change, and the mark goes when set_workspace gives it a workspace |
turnSenders | said or kept | said | Whether a turn's sender is said on what clients read: said puts who sent each turn under _meta['ahpd.sender'] on its start and its message, live and in every turn read back; kept is VS Code's, the host keeping each turn's sender for its store, its events and its usage and saying it in no action |
backendBackgroundWork | drop or say | drop | What a backend's own chat/backgroundWorkSet and chat/backgroundWorkRemoved come to: drop drops them with a line in the log, the host drawing every background row from backgroundTasks; say is VS Code's, the backend's action said as the host's own |
agentOrchestrationLimitsScope | sessionTree or session | sessionTree | What the session tools' counts of sessions and chats made and messages sent are kept per, over their sliding hour: sessionTree keeps one count for a session somebody started and every session the tools grew from it, its refusal naming the tree; session is VS Code's, each session's own count, refused in VS Code's words |
archiveDirtyWorktree | keep or commit | keep | What archiving a session does with a working tree that has uncommitted changes: keep leaves the tree, commit commits the changes on the session's branch with the message "Saving uncommitted changes before archiving session", then removes the tree and keeps the branch |
pullRequestLookup | url or branch | url | How the pass that archives and deletes merged sessions learns the state of a pull request: url asks GitHub for each related pull request by its page, a related one GitHub answers nothing for keeping the session as it is, as VS Code's lifecycle reads one; branch asks for the pull requests of the session's branch |
updateCheck | boolean | true | Ask npm, in the background, whether a newer version exists |
wire | file path | unset | Append every frame, both directions, to that file as JSON lines |
sessions | file or memory | file | Where what this host keeps about a session is written - SESSIONS.md |
automations | file or memory | file | Whether automations survive a restart, and whether their schedules fire - AUTOMATIONS.md |
unownedAutomations | every or none | every | What an automation that names no owner wakes on - AUTOMATIONS.md |
usage | { "per": "turn" | "report", "timezone": "<zone>" } | per: turn, the system's own zone | How a turn is written down, and where a day, a week and the store's days are cut - USAGE.md |
policies | { "check": true | false, "payOrder": "ending" | "regular" | "extra", "ownLimits": true | false } | check: false, payOrder: ending, ownLimits: true | Whether the rows saying who may use which agent, model and computer are enforced, which row pays first, and whether a person reads the limits that apply to them - POLICY.md |
httpApi | true, or { "port": <n>, "host": "<addr>" } | off | Serve the commands over HTTP under /api |
routes | { "port": <n>, "host": "<addr>" } | the daemon's port | Serve the plugins' routes on a listener of their own - PLUGINS.md |
mcpServers | an object of server entries | none | The MCP servers every session is offered |
plugins | a list of specs | none | The plugins loaded at startup - PLUGINS.md |
modelProxy | see PROXY.md | none | The providers this host's model proxy calls, and the model names that point at them |
Eight keys are about who may connect rather than about how the host behaves. They are connectionToken, connectionTokenFile, withoutConnectionToken, users, resource, issuer, requireIssuer and tokensPerPerson, in AUTHENTICATION.md. stdio, configFile, noPlugins, noCwd and pluginOptions mean something only when typed, so a file that sets one is warned about and ignored.
Each key that needs more than a line:
paths is the list of directories this host serves, and the first is where a session goes when a client names none, under the default workspacelessFolder. A relative one is taken from the directory of the file that set it. --no-cwd serves what is named and asks about no other folder; it is refused when the list would be empty. worktreesRoot keeps every session worktree under <dir>/<repo>/<name>, a layout of this host's own (the reference host keeps them in <repo>.worktrees beside each repository), and is made absolute against the working directory because it is a base other paths are joined to.
shellEnv is the environment every shell this host starts is given, as a map of variable name to value. A daemon that runs as a service unit has an environment of its own, and this is how the operator says which of it an agent's shells get: {"shellEnv": {"XDG_CONFIG_HOME": "/home/me/.config"}} opens every terminal and runs every Bash command with that directory. A name set to null is removed from what the daemon itself runs with, which is the other half - a shell that inherits a variable the operator does not want it to have. It reaches the host terminals and the Claude backend's Bash commands, and the other backends come later. A wrong value is ...: shellEnv.PORT must be text or null, and a name a shell cannot export is ...: shellEnv.2FA is not a shell variable name. A client that needs a variable in one session sends shellInitScripts, which is that session's own.
deltaWindowMs is how long the host gathers the streamed text of one part before sending it, in milliseconds: within that window a turn's deltas are merged into one action, so a client draws the same text from fewer envelopes. A wrong value is ...: deltaWindowMs must be an integer between 0 and 1000.
wire appends every frame, both directions, to that file as JSON lines, one message per line with an _ahpLog beside it - the shape VS Code's agent host writes its traffic log in. A line is written through the redactions VS Code's log makes: the token of an authenticate, an authorization string, the certificates of a result, and the source URL of a canvas are written as <redacted> (<redacted canvas source> for the canvas), and the data of a channel/frame or a tcp/data as <redacted channel payload>. A line over 1 MiB is written again with its strings cut and _ahpLog.truncated set; a file over 75 MiB rolls to <file>.1 and five files are kept. The capture holds every root config write a client sent, with the credentials such a write carries, so each of its files is 0600. pnpm wire -- <file> checks one against the schema.
usage says how a turn is written down and where the periods and the store's days are cut. per is turn, the default, which holds what a turn has used and writes one record when the turn ends, or report, which writes one record for every usage report so a turn still running is already billed for what it has spent. Both bill the same work, and a wrong value is ...: usage.per must be one of turn, report. timezone names, as Intl names one, the zone a day starts at and a week starts on - a week is Monday 00:00 there, not the system's own - and the zone the store keeps its days in, so a period summed there holds no record from the period before. The system's own zone is used when the key is absent or names a zone this host cannot read, which is said once at start.
The same store is served as the usage: scheme, so a client reads it through the resource calls it already has: usage:// lists the charged pools that reader may read, usage://<pool> reads that pool as { pool, kind, name, day, week, month }, usage://<pool>/range?from=&until= reads one total between two days, usage://<pool>/records?from=&until= lists the records charged to it, newest first, at most 200, usage://groups?by=&from=&until= sums the records by user, team and project, and usage://limits reads the limits that apply to the reader (USAGE.md). A pool name holds colons, so it is one encoded path segment - usage://project%3Abackend%3Asearch is project:backend:search, not an authority. ahpd usage with no pool lists what that caller may see, and with one prints that pool's three totals, cut in usage.timezone.
policies.check says whether the rows saying who may use which agent, model and computer are enforced. Off by default. The rows are kept and the policy: scheme is served either way, so a store can be filled in before anything is switched on; a wrong value is ...: policies.check must be true or false. policies.payOrder says which row of one scope pays first: ending, the default, uses the row that ends soonest first, regular the rows with no until, and extra the rows with one, the newest from first. policies.ownLimits, true by default, lets a person read the limits whose scope holds them, with only their own figure for an each limit; off, usage://limits is refused and an operator holding policy:read still reads every row through policy://<id>/limits.
httpApi serves the same commands the terminal renders, under /api. true puts it on the host's own listener; an object with a port gives it a listener of its own, and its host binds that listener - decision The HTTP API is off by default, and on the daemon's own port under /api unless httpApi.port is set, and httpApi.host binds the API's own listener. A host with neither a connection token nor a user directory refuses to start with it on, because there would be nothing to hold a request against.
mcpServers is the host's own MCP servers, by the name a person gave each, and every session is offered them whatever agent it runs. An entry is one of the two shapes VS Code uses: a stdio server needs a command, and an http one a url:
json
"mcpServers": {
"files": { "type": "stdio", "command": "mcp-server-filesystem", "args": ["/srv"], "cwd": "/srv", "env": { "TOKEN": "..." } },
"issues": { "type": "http", "url": "http://127.0.0.1:9310/mcp", "headers": { "Authorization": "Bearer ..." } }
}An entry that is neither shape, or that is missing the one its own type needs, is one warning in the log and is left out: one server nobody can reach is a gap in one agent's reach, where a wrong port is a daemon that would not run at all. A value in env or in headers is a credential, so each answers <set> here and over the API alike.
plugins names what is loaded at startup: a package, a path, or a package installed in the configuration directory. Naming one runs its code in this process with this process's permissions, which is why installing a plugin is the trust decision. A plugin is loaded once and its options make its variants, so an entry may be an object naming one with its options and enabled - PLUGINS.md.
Root config
Root config is the record a client pushes to ahp-root:// with root/configChanged, kept per host and between starts, and read back on every root state. Three kinds of key live in it. The host's own are the ones this host acts on. The settings VS Code's agent host declares are declared here in full and read back, though only six change what this host does: globalAutoApproveEnabled, terminalAutoApproveEnabled, terminalAutoApproveRules, editAutoApprovePatterns, canvasesEnabled and agentOrchestrationLimits. The daemon's are the keys of config.json a client may edit.
The host's own schema is what a client draws its controls from. A key in the schema is a promise that pushing it changes something, except a readOnly one, which a client reads and the host says.
| Key | Values | Default | What it changes |
|---|---|---|---|
defaultShell | absolute path | the system shell | The shell a host-managed terminal opens |
workspaceTrust | { "enabled": bool, "trustedUris": [uri] } | none | Which folders the host's sessions may load their projects from. readOnly: a window pushes it, it is not a control |
ahpd.restartNeeded | true | absent | Nothing: the host sets it with its own root/configChanged when a change it took waits for a restart, and a new daemon starts without it. readOnly, so a client draws no control for it. A client's push of it goes out as sent, and every reader's root holds the value pushed, as VS Code's host takes a root push as given |
Beside them the schema declares every key VS Code's agent host declares at 7516b04bc94. They come from packages/sdk/src/vscoderootconfig.ts, each with the property VS Code gives it: telemetryLevel, globalAutoApproveEnabled, showExternalSessions, agentMerge.* and the rest. Six of those are read here: globalAutoApproveEnabled, which trust.ts asks before a tool call runs; terminalAutoApproveEnabled, terminalAutoApproveRules and editAutoApprovePatterns, which host/spawn.ts answers a session's commands from; canvasesEnabled, which host/canvases.ts asks at each plugin canvas call; and agentOrchestrationLimits, which host/tooling.ts asks before the session tools check a spawn's depth or count what a tree made - TOOLS.md. canvasesEnabled is off by default, and while it is off openCanvas, updateCanvas and closeCanvas are refused - PLUGINS.md. The rest are declared and nothing more. A client draws its settings pane from the schema, so a key it cannot find is a control it cannot draw.
Three keys VS Code declared before 1.140 are not declared: artifactToolsCompactPrompts, deferredTitleGeneration and activeAgentTitleGeneration. The artifact tools carry one wording, and every session runs under the deferred title strategy.
A provider's settings are declared beside them, as VS Code registers a provider's configuration: each registration in HostOptions.providerConfigurations, which a plugin's registerProviderConfiguration fills, adds its properties to the schema at the top of the root config, its default held where nothing set the key, and the rest of it, { provider, title, description, settings, configurationFile? }, is one entry of the root's _meta['vscode.agentCustomizationSettings'], the page a client draws the provider's settings on. A key the host's own schema declares stays the host's - agentConfigurationService.ts.
Every key is one value for the whole host, which the last push from any window sets and every connection reads back, as VS Code's host keeps one root config for every window: defaultShell, workspaceTrust, terminalAutoApproveEnabled, terminalAutoApproveRules and editAutoApprovePatterns among them. The terminal rules, the edit patterns and the trust answer every session's turns whoever sent them, and a start nobody sent, as VS Code's host reads each of them for every question and every session. The declaration is decision The root config declares every key VS Code pushes, as the reference declares it, and keeps every key a client pushes: every key a push carries is applied and kept, declared or not, as VS Code's host applies a root patch, and a push that moves a backend's account is refused as a read-only property's is.
The host writes what it holds to HostOptions.rootConfigStore after each push and after an Always Allow, whole and a backend's account aside, as VS Code's host writes its own; the daemon's store is root-config.json beside config.json. A start brings back the keys the schema declares, a provider's among them, each with a value its property's type and enum accept, as VS Code's host restores its values schema by schema. Of those, globalAutoApproveEnabled, autoApprovePolicyRestricted, terminalAutoApproveEnabled, editAutoApprovePatterns, autoReplyEnabled and workspaceTrust wait for each window's next push: they are VS Code's client-owned approval and policy keys, and a value one window left could grant again what was tightened while the host was stopped. A host given no store holds its root config for as long as the process runs.
One value is in config.values and in no schema at all: the account each agent's harness is signed in as, under vscode.codexAccount for Codex - the key VS Code's window reads the account of its own host's Codex agent from - and ahpd.<provider>Account for every other backend. Nobody declares it, and only the host moves it: the host writes it when a probe answers with an account or a running session reports one, and a client's push that moves it is refused in the words VS Code's host refuses a remote client's change to a read-only property, Host configuration property is read-only: <key>. The email in it is the operator's own, so under a people directory a connection that is not root and does not hold account:read is shown no account value at all, in the state it subscribes to and in every change after it; a host with no directory shows it to every connection.
The daemon's half is added to the same schema, and config:read is what a connection needs to be shown it. It is paths, port, host, httpApi, routes, modelProxy, updateCheck, advancedTools, sessionTools, sessionToolsProviders, closedChats, unofferedModel, workspacelessFolder, turnSenders, backendBackgroundWork, agentOrchestrationLimitsScope, archiveDirtyWorktree, pullRequestLookup, wire and mcpServers, and each configured plugin is one more key, plugins.<id> by its id, whose value is { enabled, options } - so one plugin is one key and its variants are its own options. Nothing else the file holds is there: stdio, configFile, noCwd, worktreesRoot, clientToolTimeoutMs, the connection token keys, requireIssuer, issuer, resource, users, automations and sessions are still edited the way they always were.
Pushing a key acts in one of two ways, and the answer says which.
advancedTools,sessionTools,sessionToolsProviders,closedChats,unofferedModel,workspacelessFolder,turnSenders,backendBackgroundWork,agentOrchestrationLimitsScope,archiveDirtyWorktree,pullRequestLookup,wireandmcpServersapply to this daemon as written.advancedToolschanges the tools every running session's model is offered at once; the two session tool keys are rebuilt per session, since a provider list is not the same answer for all of them, and each running session is told its own set; the wire capture starts, moves or stops;mcpServersis read again for the next session opened, while a running session keeps the servers it started with.closedChatsis read when the next chat is closed, so no session is rebuilt and none is told.unofferedModelis read as the next turn is sent.workspacelessFolderis read as the next session is made.archiveDirtyWorktreeis read when the next archived session gives its tree back.pullRequestLookupis read by the next pass over merged sessions.- Every other key is written to
config.jsonand the answer marks a restart as needed: the root's config carriesahpd.restartNeeded: true, set by the host's ownroot/configChanged, which every reader of root is shown whether or not it may see the keys the notice is about. A connection watching the root is also sentx-ahpd/restartNeededwith{ channel: "ahp-root://" }once, when the mark is set: it is anx-ahpdextension, which a client that does not know it ignores.ahpd restartapplies it.
A write to the daemon's file that no client pushed, a plugin switched, installed or removed through x-ahpd-plugins or ahpd plugin, or a folder added or removed through ahpd paths, is said on the root too: the host sends the root/configChanged that carries every daemon key the file now answers differently, and null for a key it no longer holds, so a client that watched and one that subscribes afterwards read the same values. The schema is the one the root opened with, since no protocol action carries a schema, so a plugin installed while the daemon runs has its value in config.values and its property in the schema from the next start.
That split, and the reason it is per key rather than all-or-nothing, is decision A configuration change applies live where the key can, and otherwise on ahpd restart.
A key shows what the file holds rather than what this run is using, and when a start flag overrode it the key's description says so. A credential is never sent back: every value a schema marks writeOnly, or secret, however deep in its options the mark sits, is answered as <set>, here and over the API alike, and a client that sends that back has said the credential is left as it is. A client drawing a record's form reads the same two keywords off the record's schema: a field marked secret is one a read answers as { "$secret": "<name>" }, so the field is drawn as set and the value is never asked for again.
Commands
The verbs that read or change this host's own settings, one line each. The rest are REFERENCE.md.
| Command | What it does |
|---|---|
ahpd config | Print every file it read, then each key and its value, each naming the file that set it |
ahpd configure | Ask at the terminal for each setting a first install needs, and write them |
ahpd status | Say whether a daemon is running, and where |
ahpd restart | Stop it and start it again with the same line, which is what applies a change that is not live |
Grants
| What | Grant | Where it is asked |
|---|---|---|
| Read the daemon's own settings in root state | config:read | seesConfig, and the status command |
| List the plugins the configuration names | plugin:list | The plugin list command, and x-ahpd-plugins |
| List the adapters the host serves | connector:list | x-ahpd-adapters |
| List the engines the host serves | agent:list | x-ahpd-engines |
| Subscribe to the connectors channel | connector:list | x-ahpd-connectors |
| List the caller's own, their teams' and the host's connectors | connector:list | x-ahpd-connectors, and no other person's connector with it |
| List every connector | connector:all | x-ahpd-connectors, and it is admitted without connector:list |
| List the connector choices of a form field | connector:list, or connector:all | x-ahpd/completions, and the host's and the caller's own connectors only |
| Make or change a connector | connector:put, and connector:host for a host connector | x-ahpd/connectors/put |
| Remove a connector | connector:remove, and connector:host for a host connector | x-ahpd/connectors/remove |
| Check a connector | connector:put, and connector:host for a host connector | x-ahpd/connectors/check |
| Change a root config key | config:change | root/configChanged, and a replace - config:write covers it |
Push workspaceTrust | trust:push | The same action, when workspaceTrust is the one key it carries - trust:write covers it |
| Read the account an agent bills to | account:read | seesAccount, on the root state a connection subscribes to and on every root/configChanged after it |
| Stop the daemon | config:change | The shutdown command |
| Edit the configuration over HTTP | config:write | GET /api/config; the plugin verbs that write take plugin: grants, and install, update and remove are the deployment token's |
See also
| REFERENCE.md | The process: its verbs and its flags |
| CONFIGURE.md | How the daemon reads its configuration |
| AHP.md | The root channel's actions and their origins |
| AUTHENTICATION.md | The door, the tokens and the seven keys about who may connect |
| RESOURCES.md | The schemes this host serves and who may read them |
| PLUGINS.md | What a plugin contributes to a host |