Agent-First PSQL v0.6.2: Container Transport Family and Self-Describing Sessions

by Agent-First Kit Contributors

v0.6.2 generalizes the docker transport into a container transport family (podman, nerdctl, compose, kubectl) with structured scope flags and SSH chaining, adds a session_info pipe request so agents can introspect their session's transport, permission default, and limits instead of probing with failing queries, and surfaces two new log events for implicit behaviors that previously had to be inferred.

Agent-First PSQL v0.6.2 builds on v0.6’s SSH transport and v0.6.1’s SQLSTATE-on-connect work along two threads: the container boundary becomes a first-class transport family, and pipe sessions become self-describing for the agent that uses them.

Container transport, generalized

v0.6 had docker transport. v0.6.2 generalizes that into a container transport family that covers the runtimes agents actually encounter:

--container-driver docker     (default)
--container-driver podman
--container-driver nerdctl
--container-driver compose
--container-driver kubectl

For Docker contexts, kubectl namespaces, Compose files, and multi-container Kubernetes pods, the scope is set via named flags rather than passed through as raw driver argv:

afpsql --container app-pod --container-driver kubectl \
  --container-namespace prod --container-pod-container postgres \
  --host 127.0.0.1 --port 5432 \
  --user app --dbname appdb --password-secret-env PGPASSWORD \
  --sql 'select 1'

The point of the named flags is that the agent never builds a runtime command line by string concatenation. afpsql is the one that emits kubectl exec app-pod -c postgres -i -- .... An agent that runs raw kubectl exec ... -- psql is back to parsing human output and shell state; the structured contract goes through afpsql.

The permission family is container-read (default) / container-write, parallel to the existing read/write and ssh-read/ssh-write families. A container session that requests --permission write is rejected before execution with a hint to use container-write, the same way --permission write --ssh ... was already rejected in v0.6.

Container over SSH: one local chain

For containers on a remote SSH host, --ssh and --container compose into one local afpsql transport chain. The agent stays local; afpsql runs the container exec command on the SSH host and bridges from inside the container:

afpsql --ssh root@server --container app-container \
  --container-driver docker \
  --host postgres --port 5432 \
  --user app --dbname appdb --password-secret-env PGPASSWORD \
  --sql 'select 1'

This is one chain, not “SSH in, then run psql.” The permission family stays container (container-read / container-write) because the database boundary is still the container, not the SSH host. Enable --log transport to see the resolved chain on each new session, e.g. ssh:root@server -> docker exec app-container -> tcp postgres:5432.

Container bridge prerequisites are the same regardless of driver: the target container needs sh plus one of python3, python, or perl. The container does not need afpsql or psql installed.

session_info: introspect, don’t probe

In pipe mode, the agent now asks a session what it is, rather than finding out by sending a query that fails:

{"code":"session_info","session":"work"}
{
  "code": "session_info",
  "session": "work",
  "transport_kind": "container",
  "permission_default": "container-read",
  "stream_rows_default": false,
  "batch_rows": 1000,
  "batch_bytes": 262144,
  "inline_max_rows": 1000,
  "inline_max_bytes": 1048576,
  "statement_timeout_ms": 30000,
  "lock_timeout_ms": 5000,
  "trace": {"duration_ms": 0}
}

That is the agent’s “what am I connected to” question, answered without spending a query. transport_kind and permission_default tell the agent which permission family the next write needs; inline_max_rows / inline_max_bytes tell it whether the upcoming result will be inline or streamed; the timeouts tell it which queries are about to be cut off.

The skill’s standing guidance in pipe mode is now: send session_info once before running queries, rather than spending a turn on afpsql --help or sending probe queries to learn the same facts.

Implicit behavior, made visible

Two failure modes that previously had to be inferred from outcomes now emit dedicated log events:

--log mode      mode.permission_default_changed
--log connect   connect.libpq_env_fallback

mode.permission_default_changed fires whenever --mode psql bypasses the native read-only default. psql-mode keeps psql’s writable default for non-interactive script compatibility, but the agent that translated a script to afpsql still needs to know it just dropped the write boundary.

connect.libpq_env_fallback lists which libpq PG* environment variables (PGHOST, PGPORT, PGUSER, PGDATABASE, PGPASSWORD, PGSSLMODE) filled connection fields the agent did not pass via flags or secrets. An agent that thought it was connecting to 127.0.0.1 while a stale PGHOST in the parent shell pointed elsewhere now sees that on the log channel before the query runs, not after the wrong row comes back.

Container bridge: hardened handshake

The container exec bridge that v0.6 introduced now uses a per-connection 16-byte hex nonce on its AFPSQL_BRIDGE_OK ready banner. A container init script that happens to print the literal string AFPSQL_BRIDGE_OK to stdout can no longer trip the handshake. Captured bridge stderr placed on ConnectError.hint is also sanitized — control characters except \n / \t mapped to space, hint capped at 512 bytes — so ANSI sequences and embedded NULs from a noisy container cannot reach structured logs.

Hints, everywhere

Every Output::Error path now carries an actionable hint. The hint-on-every-error contract was already true for connection, permission, and validation paths in v0.6 / v0.6.1; v0.6.2 backfills the remaining ones (cancellation, internal, duplicate id, finished, missing id, invalid params). An agent that branches on error_code always has a sentence telling it what to try next.

Adoption

brew install agentfirstkit/tap/afpsql   # macOS / Linux
cargo install agent-first-psql          # any platform

afpsql skill install
afpsql skill status

For the embedded skill and SQLSTATE-on-connect work this release builds on, see the v0.6.1 release post.