aster.ax / Docs

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:

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:

  1. Publish the root-signed caller grant and wait for capabilities to report the host node as the target with backend_ready: true.
  2. Require connection: connected.
  3. Submit cmd.exe /C echo ax-deploy-<request-id> under the open profile with the host's reported default working directory.
  4. 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:

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

Limitations

Next steps