# What agentless means

Opafra connects over ordinary SSH and installs nothing. Here is exactly what it needs on a host, and what it never asks for.

Source: https://opafra.com/docs/inventory/agentless-ssh

---

Opafra runs commands over the same SSH your own terminal uses. There is no agent, no
daemon, and no package to install on a target host. A server you register today can be
deregistered tomorrow with nothing left behind on it.

## What the host needs

Three things, all of which a Linux host already has:

- **An SSH server** accepting connections on a port Opafra can reach
- **A user account** to connect as
- **A public key in that user's `authorized_keys`**, or another accepted credential

That is the whole requirement. No Python, no runtime, no bootstrap step. The host does not
dial out, does not poll, and does not need to reach Opafra at all: every connection is
made inbound, by Opafra, when a run needs it.

## What it does not need

- No agent process, and so nothing to keep running, patch, or monitor
- No inbound access from the host to Opafra
- No root user. A normal account with the privilege the plans need is enough
- No changes to the host's configuration beyond the one `authorized_keys` line

> **Note**
>
> This is the trade-off Opafra takes deliberately. An agent can watch a host continuously
> and act when it is offline. Opafra cannot, and does not claim to. What it gets in exchange
> is that adopting it costs one line on one host, and abandoning it costs deleting that line.

## Privilege

Opafra connects as the user you registered, with exactly that user's rights. Where a step
needs more, it uses `sudo`, and **`sudo` must not prompt for a password**: a run is not an
interactive session and cannot answer a prompt.

The connection check tests this and reports it as a warning rather than a failure, because
plans that never elevate are perfectly valid. If your plans do need privilege, grant it in
`sudoers` narrowly rather than broadly. `NOPASSWD` on the specific commands the host is
actually managed with is much better than `NOPASSWD: ALL`, and Opafra works the same
either way.

## Host keys

Opafra verifies the host key it is presented. A key that changes between connections is
reported as a mismatch rather than accepted silently, because that is indistinguishable
from an interception, and a run that connects anyway would execute your commands on
whatever answered.

If you rebuilt the host and the key legitimately changed, resolving it is a deliberate act
you perform on the server record.

## Network direction

Every connection is outbound from Opafra to your host. That has two consequences worth
planning around:

- The host needs to accept SSH from wherever Opafra runs. For a host on a private network,
  that usually means reaching it through a [bastion](/docs/inventory/servers).
- A host that is powered off or unreachable is simply not runnable. There is no queue on
  the host that drains when it comes back, because there is nothing on the host.

## Credentials never reach the plan

A plan names a server; it does not carry that server's credential. The credential is
resolved at connection time from the encrypted record or from your secret provider, used,
and discarded.

This is why a plan is safe to read, share, review in a pull request, and store in git. The
plan text contains no key material and no passwords, and a `secret://` reference in an
argument is redacted everywhere a command is displayed or logged, including in a
[dry run](/docs/running/dry-run) preview.

> **Warning**
>
> The one thing agentless cannot protect you from is a command you wrote yourself.
> `ssh.cmd` runs whatever text it is given, with the privilege the connecting user holds.
> Prefer a named tool where one exists, and dry run before the first real run.

## Next steps

- [Registering servers](/docs/inventory/servers) to add the first host
- [Environments](/docs/inventory/environments) for grouping hosts and attaching policy
