Windows host reference
ax deploy ssh installs a native Windows runner from a Linux or macOS owner
machine. Use this reference to look up the files, configuration, scheduled task
and limits behind that deployment. The
SSH deployment guide walks through the
deployment itself.
Paths
Deployment manages the default profile, and every path below belongs to that
profile.
| Purpose | Path |
|---|---|
| Launcher | %LOCALAPPDATA%\ax\bin\ax.cmd |
| Versioned executable | %LOCALAPPDATA%\ax\versions\<SHA256>\ax.exe |
| Release pointer | %LOCALAPPDATA%\ax\current |
| Host configuration | %LOCALAPPDATA%\ax\server.json |
| Captured host log | %LOCALAPPDATA%\ax\host.log (rotated once to host.log.1 above 1 MiB) |
| Deployment record | %LOCALAPPDATA%\ax\deployment.json |
| Node key | %USERPROFILE%\.ax\keys\default.node.key |
| Registration grant | %USERPROFILE%\.ax\keys\default.registration-grant.json |
| Enrollment credential | %USERPROFILE%\.ax\keys\default.enrollment.chain |
| Discovery record | %USERPROFILE%\.ax\messaging\default\discovery.json |
| Host state directory | %USERPROFILE%\.ax\messaging\default |
| Exec readiness record | <state>\exec\readiness.json |
| Routing address record | <state>\address.json |
| Job records and output | <state>\exec\jobs |
The node key is generated on the host and never leaves it. Only its public node id crosses the SSH connection. The owner root private key, the namespace write secret and the deploying caller's credential stay on the owner machine. The configure request carries three things: public trust values, the host's own enrollment chain and the root-signed registration grant.
Configuration
Deployment writes and merges server.json. It is not written by hand.
Deployment refuses a file with unknown fields, a file that is
not private, or a file with a different identity, and leaves it unchanged.
The transport facts use the same names as on a Unix host, at the top level.
The one transport key that only Windows has is the peer_transport mode
value:
peer_transport |
Written by | Transport facts |
|---|---|---|
true |
Deployment | Every fact is required. |
false |
ax admin setup |
None of the facts is allowed. |
{
"state_directory": "C:\\Users\\owner\\.ax\\messaging\\default",
"root_public_key": "<64 hex>",
"node_key_file": "C:\\Users\\owner\\.ax\\keys\\default.node.key",
"messaging": {
"grant_file": "C:\\Users\\owner\\.ax\\keys\\default.registration-grant.json",
"inbox": "C:\\Users\\owner\\.ax\\messaging\\default\\inbox.db",
"registration_socket": "C:\\Users\\owner\\.ax\\messaging\\default\\registration.sock",
"registration_principal": {"platform": "windows", "sid": "S-1-5-21-...", "kind": "user"},
"discovery_file": "C:\\Users\\owner\\.ax\\messaging\\default\\discovery.json"
},
"peer_transport": true,
"bind_address": "0.0.0.0:0",
"policy_namespace": "<64 hex>",
"policy_replicas": [],
"audience": "ax-exec",
"credential_file": "C:\\Users\\owner\\.ax\\keys\\default.enrollment.chain",
"standalone_policy": true,
"disable_relays": false,
"disable_discovery": false
}
A local-only configuration written by ax admin setup carries only the mode:
{
"state_directory": "...", "root_public_key": "...",
"node_key_file": "...", "messaging": {"...": "unchanged"},
"peer_transport": false
}
Redeployment treats the fields in three groups:
| Fields | On redeploy |
|---|---|
| Identity and trust: state directory, root public key, node key, messaging grant, socket and discovery file, and account | Must match exactly. A difference is refused with the changed field named, and the file is not overwritten. |
Transport identity and trust: policy_namespace, audience, credential_file, standalone_policy |
Created when absent, then fixed. |
Routing and access: bind_address, policy_replicas, the relay and discovery switches |
Created when absent, then kept from the existing configuration, so operator edits survive a redeploy. |
The standard single-host deployment writes the standalone read-only policy
shape on both platforms: standalone_policy: true, policy_replicas: [] and
no read ticket. The host imports the fleet namespace read-only, and Exec is
composed before policy sync. A locally writable namespace is refused at
startup with the same assertion the Unix host uses.
A deployment never writes policy_read_ticket_file, and the key is omitted
when absent. Whether a read ticket is valid depends on the policy mode:
- In a standalone configuration (
standalone_policy: true), a read ticket is refused at validation withpolicy_read_ticket_filenamed. The error names the recovery: remove the field, or reset the host withax undeploy ssh --purgeand thenax deploy ssh. - In replica mode (
standalone_policy: falsewith policy replicas), an absolute read ticket is valid bootstrap material. The host passes it to Aster as the fleet ticket.
inbox is a retained legacy field. The v1 store lives below the state
directory and never opens it.
Writes are atomic and private. Deployment commits the configuration after the grant, the credential and the discovery record, so an interrupted deployment retries without losing signed authority.
ax admin setup on the host writes a local-only configuration with
peer_transport: false. It refuses to replace an existing file that differs.
If the parsed transport mode shows that deployment manages the host, admin
setup refuses before it generates a second root, and points you to deployment
from the owner machine.
Migrating a pre-16 configuration
The current configuration shape has no nested peer_transport section. A
configuration written in the earlier nested shape does not parse, and there is
no backward compatibility. The host and ax admin setup refuse it with a
message that names the reset. To recover, remove the deployment and deploy
again from the owner machine:
ax undeploy ssh user@windows-host --purge
ax deploy ssh user@windows-host --name build
--purge also removes the managed host state, so the redeploy issues a fresh
host identity, enrollment credential and registration grant.
Scheduled task
Deployment registers the per-user task ax-host. A full deployment also
starts it.
| Setting | Value |
|---|---|
| Action | cmd.exe /D /S /C ""<launcher>" serve --config "<server.json>" >> "<host.log>" 2>&1 |
| Principal | The installing account, Interactive, Limited |
| Trigger | At that user's logon |
| Execution time limit | None |
| Restarts | Three, one minute apart |
| Multiple instances | MultipleInstances IgnoreNew |
Installation registers the task without starting it. The full deployment restarts it so that the new credential and routing address take effect.
ax deploy identifies its own task by the task's description, principal SID and
action. A foreign task with the same name is refused, and --replace takes it
over. A task with the previous launcher-direct action is accepted as one of
ax's own and rewritten.
Removing the task
ax undeploy ssh HOST stops the managed task and the host process that serves
the configured server.json, then unregisters the task. Stopping the owned
task's host is part of undeploying it.
What removal does with each kind of task or process:
| Found on the host | Removal |
|---|---|
No ax-host task |
Not an error. |
A foreign or modified ax-host task |
Blocks removal. |
| An owned task | Removal collects and stops every managed host that serves that configuration, without --stop-services. |
A managed host process that serves the configured server.json but is not covered by an owned task, when no owned task exists |
Blocks removal until --stop-services permits stopping it. |
An installed ax.exe process that does not serve the configured server.json |
Always blocks removal. Stop it yourself. |
--stop-services never kills clients and never cancels jobs.
--purge also removes %USERPROFILE%\.ax, %LOCALAPPDATA%\ax\server.json,
host.log, host.log.1 and deployment.json. The normal installation removal
deletes the launcher, the version directories and the launcher directory
itself.
Provisioning lock
Removal takes the provisioning lock, so it cannot run in the middle of a deployment. The first two cases below are taken over, so that a crashed deployment or removal does not block later runs. The other cases fail closed:
| Lock state | Handling |
|---|---|
| Recorded owner is dead, or its PID was reused | Taken over. |
| No owner record, and older than an hour | Taken over. |
| Owner process still exists, with a matching creation time or none recorded | Never taken over, whatever its age. |
| Owner is live but its creation time cannot be read | Unverifiable. Never taken over, whatever its age. |
| Owner record present but unreadable, malformed or shared | Refused in place. Never taken over by age. |
A wedged but live owner blocks removal until that process is stopped. In each case that is never taken over, remove the lock by hand only after you have inspected it and confirmed that the provisioning process is gone.
Verification and readiness
exec_ready: true is set only after an authenticated Exec v1 diagnostic:
- Publish the root-signed caller grant and wait for
capabilitiesto report the host node as the target withbackend_ready: true. - Require
connection: connected. - Submit
cmd.exe /C echo ax-deploy-<request-id>under theopenprofile with the host's reported default working directory. - Poll status to a terminal state, then require complete, untruncated stdout chunks containing exactly the token.
The readiness record is a startup diagnostic. Deployment uses it to prove that
the process has the prepared identity, account and credential. It never sets
exec_ready. Deployment saves the diagnostic request id before it writes the
configuration. An interrupted deployment therefore either reuses the retained
job or resubmits a byte-identical request.
Identity separation
Four identities stay distinct:
- Transport identity: the Aster node key generated on the host.
- Logical agent identity: mailbox keys registered locally under the host configuration's grant.
- Owner identity: the deploying profile's root. Its private key never leaves the owner machine.
- OS execution identity: the Windows account SID, reported by the host token, written to the configuration, readiness record and grant account alias.
The host treats a message that arrives at it as data. Enrollment does not grant
execution permission. The host composes Exec with credential_scopes: false,
so a caller needs the root-signed ExecGrantV1. The open profile inherits
the account's OS permissions and is not a sandbox.
Redeploy
- The SSH pin, deployment record and host journal preserve the node id, account and grant identity. A changed prepared identity, root, namespace or caller is refused.
- The grant is idempotent, and an identical root record is not rewritten. The
runner result reports
authorization: {grant_revision, changed}.changedis true only when this deployment created the grant. - Each deployment issues a fresh seven-day host credential and restarts the task, because the Windows host has no credential-reload watcher.
- The authenticated diagnostic is resubmitted only after a known terminal outcome. A pending or ambiguous job keeps its request id.
- A running job stays observable across a host process or task restart because its supervisor is detached. It does not survive a reboot or logoff.
Limitations
- The owner machine must be Linux or macOS. The Exec client broker runs only
on Unix, so a Windows owner can use only
--planand--install-only. - The task runs in the signed-in user's interactive session at logon. It does not run before logon, as a session-0 service, or on a machine with no logged-in user.
- Cancellation terminates the job's process tree immediately, because Windows has no graceful console signal. A requested grace period is not honored.
- Workspace transfer (
ax exec run --workspace) is not composed on the native Windows host. WSL runners use the Linux backend and support their own path. - A running job survives a host restart but not a reboot or logoff.
- The Windows backend does not prune retained request or output records by age. They accumulate until removed manually, and the target advertises no time-based retention.
- Peer connectivity may be direct or through the configured relay. The deployment does not open a firewall port. The host binds the configured address and advertises its reachable addresses.
- A configuration created by
ax admin setup(peer_transport: false) serves messaging and the local named pipe only until it is deployed from an owner machine.
Next steps
- The SSH deployment guide walks through deploying a native Windows runner.
- Updating ax: update ax on deployed hosts.
- The security model describes the checks that apply on every runner.