Protocol Reference
Every stdin/stdout line is one JSON object with required code.
- pipe mode: full protocol with
idcorrelation - CLI mode: same event schema,
idmay be omitted in display output - protocol events are emitted on
stdoutonly stderris not part of the runtime protocol contract
Interface Boundary
This protocol is the only runtime interface.
psql modeis CLI argument translation only; runtime protocol is unchanged- no legacy text interpolation
- no table/text output contract
Input (stdin)
query
Execute one SQL statement.
| Field | Required | Description |
|---|---|---|
code | yes | "query" |
id | yes | client correlation id |
session | no | session id; default session if omitted |
sql | yes | SQL text |
params | no | positional bind values |
options | no | query behavior |
options fields:
| Field | Default | Description |
|---|---|---|
stream_rows | false | stream rows as result_rows events |
batch_rows | 1000 | max rows per result_rows event |
batch_bytes | 262144 | soft byte target per streamed batch |
statement_timeout_ms | config default | per-query statement timeout |
lock_timeout_ms | config default | per-query lock timeout |
read_only | false | enforce read-only transaction for this query |
inline_max_rows | config default | inline row cap for non-streaming |
inline_max_bytes | config default | inline payload bytes cap for non-streaming |
Parameter Binding Rules
- Dynamic values should be passed via
paramswith$1..$Nplaceholders. - Placeholder count must equal
paramslength (validated from prepared-statement metadata, not SQL text scanning). - Count/type validation failures return
error_code: "invalid_params".
Driver-side type mapping (prepared statement parameter OIDs):
bool-> JSON bool or"true"/"false"int2/int4/int8-> JSON integer or numeric stringfloat4/float8/numeric-> JSON number or numeric stringjson/jsonb-> JSON object/array/scalar- others -> text form (
stringpreferred)
Unsupported:
:nameinterpolation- SQL string template expansion by client-side substitutions
CLI mapping notes:
--param N=valuemaps to thisparamsarray- in
psql mode, numeric-v N=valuemay be translated toparams[N]
config
Partial runtime config update. Echoes full config afterward.
| Field | Required | Description |
|---|---|---|
code | yes | "config" |
default_session | no | default session name |
sessions | no | session connection definitions |
inline_max_rows | no | global inline row limit |
inline_max_bytes | no | global inline payload bytes limit |
statement_timeout_ms | no | global statement timeout |
lock_timeout_ms | no | global lock timeout |
log | no | enabled log categories |
Session connection shape supports:
dsn_secretconninfo_secrethostportuserdbnamepassword_secret
TLS settings supplied in dsn_secret or conninfo_secret (for example
sslmode=require) are honored by the PostgreSQL driver.
CLI translation notes:
- agent-first mode uses direct agent-first flags (
--dsn-secret,--host, …) psql modemay translate legacy flags (-h,-p,-U,-d,-c,-f) into these same canonical fields
cancel
Cancel an in-flight query by id.
{"code":"cancel","id":"q-123"}
When the database connection is already established, afpsql sends a
PostgreSQL server-side cancel request before aborting local output handling.
Cancellation is still race-prone: a query may finish normally before the cancel
request is processed.
ping
Health check.
{"code":"ping"}
close
Graceful shutdown.
{"code":"close"}
Output (stdout)
result
Small result returned inline.
| Field | Description |
|---|---|
code | "result" |
id | query id |
session | session used |
command_tag | Normalized command tag (ROWS N / EXECUTE N) |
columns | column metadata array |
rows | result rows |
row_count | row count |
trace | timing and counters |
result_start
Start of streamed result.
| Field | Description |
|---|---|
code | "result_start" |
id | query id |
session | session used |
columns | column metadata |
result_rows
One streamed row batch.
| Field | Description |
|---|---|
code | "result_rows" |
id | query id |
rows | row objects for this batch |
rows_batch_count | rows in batch |
result_end
End of streamed result.
| Field | Description |
|---|---|
code | "result_end" |
id | query id |
session | session used |
command_tag | Normalized command tag (ROWS N / EXECUTE N) |
trace | includes duration_ms, row_count, payload_bytes |
sql_error
Database execution error.
| Field | Description |
|---|---|
code | "sql_error" |
id | query id |
session | session used |
sqlstate | SQLSTATE (23505, 42P01, …) |
message | primary error message |
detail | optional detail |
hint | optional hint |
position | optional SQL character position |
trace | timing and counters |
error
Client/runtime/protocol error.
| Field | Description |
|---|---|
code | "error" |
id | optional related query id |
error_code | machine-readable code |
error | human-readable detail |
retryable | whether retry may succeed |
trace | timing and counters |
Canonical error_code values:
invalid_requestinvalid_paramsconnect_failedresult_too_largecancelled
Other output codes
code | Meaning |
|---|---|
config | full runtime config echo |
pong | ping response with counters |
close | shutdown acknowledgement |
log | optional runtime diagnostic event (enabled by log config/categories) |
log event fields:
event(e.g.query.result,query.error,query.sql_error)request_id(optional)session(optional)error_code(optional)command_tag(optional)trace
Startup log events include parsed args, redacted config, and selected
redacted environment fallback fields. They intentionally omit raw argv.
Bind values are summarized as param_count, not logged as plaintext.
log category matching (from config.log / --log):
- empty list disables
logevents allor*enables all categories- exact match (
query.result) - group prefix match (
query->query.*)
Runtime Safety Limits
Pipe mode applies hard protocol limits before executing a request:
- max JSONL line: 1 MiB
- max SQL text: 1 MiB
- max params per query: 65,535
- max concurrent in-flight queries: 64
Environment Fallback
Optional runtime fallback variables:
AFPSQL_DSN_SECRETAFPSQL_CONNINFO_SECRETAFPSQL_HOSTAFPSQL_PORTAFPSQL_USERAFPSQL_DBNAMEAFPSQL_PASSWORD_SECRET
Standard PostgreSQL environment fallback (lower precedence):
PGHOSTPGPORTPGUSERPGDATABASE
Example: Small Result
Input:
{"code":"query","id":"q1","sql":"select 1 as n"}
Output:
{"code":"result","id":"q1","command_tag":"ROWS 1","columns":[{"name":"n","type":"int4"}],"rows":[{"n":1}],"row_count":1,"trace":{"duration_ms":2}}
Example: Streamed Result
Input:
{"code":"query","id":"q2","sql":"select * from big_table where id > $1","params":[100],"options":{"stream_rows":true,"batch_rows":1000}}
Output:
{"code":"result_start","id":"q2","columns":[{"name":"id","type":"int8"},{"name":"name","type":"text"}]}
{"code":"result_rows","id":"q2","rows":[{"id":101,"name":"a"},{"id":102,"name":"b"}],"rows_batch_count":2}
{"code":"result_end","id":"q2","command_tag":"ROWS 200000","trace":{"duration_ms":443,"row_count":200000,"payload_bytes":34199211}}