# Hosting-agent integration contract

This is the entry point for software agents, control-plane services and human
integrators. The canonical machine-readable transport contract is
[`../asyncapi.yaml`](../asyncapi.yaml); operation payloads and terminal results
are versioned in [`../schema/`](../schema/).

## Trust boundary

The hosting agent is a privileged executor, not an authorization service. A
control plane must authenticate the requesting principal, authorize the tenant,
operation, resource and target host, record the job and outbox entry, and only
then publish one command.

RabbitMQ authenticates connections and limits which exchanges and queues an
account may access. The deployed hosting-agent worker account can consume only
its host queue and publish only its own results. A control-plane publisher
should be able to write `hosting-agent.commands` and must not consume host
command queues.

The worker does not authenticate the envelope's `source` or `tenant_id` and
does not enforce a per-source or per-tenant ACL. Those strings are audit and
correlation data, not verified identities. The current security model therefore
requires a trusted, singular control-plane publishing boundary. Do not give an
AI agent, browser or tenant application direct command-exchange credentials.

After broker authorization, the agent independently enforces:

- an exact `subject` match for its immutable `host_id`;
- a compiled allowlist of versioned command types;
- command creation, expiry and maximum-validity checks;
- strict operation payload decoding;
- root-managed capability manifests for services, runtimes and certificates;
- bounded inputs, locks, idempotency and a durable terminal-result ledger.

Knowledge of a command or schema never grants permission to execute it.

## RabbitMQ contract

The transport is AMQP 0-9-1 over TLS in the `/hosting` virtual host.
Environment-specific broker addresses and credentials are supplied out of band
and are deliberately absent from the public contract.

| Purpose | Exchange | Type | Routing key | Queue pattern |
|---|---|---|---|---|
| Commands | `hosting-agent.commands` | topic | `host.<host_id>` | `hosting-agent.<host_id>` |
| Terminal results | `hosting-agent.results` | topic | `host.<host_id>` | `hosting-agent.results.<host_id>` |
| Rejected commands | `hosting-agent.dead` | topic | `host.<host_id>` | `hosting-agent.dead.<host_id>` |

Commands and results use `application/json` and persistent delivery. A
publisher must use the exact host routing key, set a unique AMQP `message_id`
equal to the envelope `id`, and use publisher confirmation before marking its
outbox item as sent. The AMQP `correlation_id` should equal the envelope
`correlation_id`.

The worker uses prefetch 1. It publishes a confirmed terminal result before it
acknowledges the command. Retryable local infrastructure failures are requeued.
Validation and terminal operation failures produce a `failed` result and the
original command is rejected without requeue, causing RabbitMQ to dead-letter
it. A repeated command ID with identical content returns the durable recorded
result; reuse of an ID with different content is rejected.

Consumers must correlate on `command_id`, tolerate duplicate terminal results,
and treat the PostgreSQL job/outbox record rather than RabbitMQ queue presence
as authoritative workflow state.

## Command catalogue

| Type | Data schema | Effect | Host-local capability source |
|---|---|---|---|
| `hosting.webspace.ensure.v1` | `webspace-ensure-v1.json` | mutating | web root and slot inventory |
| `hosting.health.inspect.v1` | `health-inspect-v1.json` | read-only | `health.json` |
| `hosting.service.inspect.v1` | `service-inspect-v1.json` | read-only | `services.json` |
| `hosting.workload.quiesce.v1` | `service-resource-operation-v1.json` | mutating | `services.json` |
| `hosting.workload.resume.v1` | `service-resource-operation-v1.json` | mutating | `services.json` |
| `hosting.volume.mount.v1` | `service-resource-operation-v1.json` | mutating | `services.json` and `/etc/fstab` |
| `hosting.volume.unmount.v1` | `service-resource-operation-v1.json` | mutating | `services.json` and `/etc/fstab` |
| `hosting.runtime.inspect.v1` | `runtime-inspect-v1.json` | read-only | `runtimes.json` |
| `hosting.agent.update.inspect.v1` | `agent-update-inspect-v1.json` | read-only | `update.json` |
| `hosting.tls.inspect.v1` | `tls-operation-v1.json` | read-only | `tls.json` |
| `hosting.tls.ensure.v1` | `tls-operation-v1.json` | mutating | `tls.json` and getssl configuration |

The table lists compiled protocol support. A particular host can execute only
resources present in its root-managed local configuration. FTP-account and
database provisioning commands are not implemented in protocol version 1.

## Result semantics

Every accepted command produces one terminal result matching
[`result-envelope-v1.json`](../schema/result-envelope-v1.json):

- `planned`: a local CLI dry-run completed and reports proposed actions;
- `succeeded`: the operation reached its requested state; inspect `changed` to
  distinguish a mutation from an idempotent no-op;
- `failed`: validation or execution failed; `error` contains a sanitized human
  description.

The RabbitMQ worker currently executes real operations and does not expose a
remote dry-run flag. Use the root CLI dry-run as an operator-controlled test
path. Intermediate `accepted`, `started` or `progress` events are not currently
published.

## Safe integration sequence

1. Resolve the target host from the internal inventory; never infer a host ID
   from a hostname.
2. Validate the complete envelope and operation data locally against the
   published schemas.
3. Authorize principal, tenant, operation, resource and target host in the
   control plane.
4. Persist the job and transactional outbox record.
5. Publish with confirmation and retain the command ID until a terminal result
   is projected.
6. Consume and validate the terminal result, correlating it by `command_id`.
7. Continue only with the explicitly documented next workflow step. Webspace
   creation, TLS, Apache activation and DNS remain independent operations.

For a new PHP webspace, first use the local CLI dry-run required by the hosting
workspace policy. The first safe RabbitMQ smoke test should be a read-only
`hosting.health.inspect.v1` command. Do not use a mutating command merely to
test connectivity.
