> ## Documentation Index
> Fetch the complete documentation index at: https://docs.codezero.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Database Connections

> Inject credentials into PostgreSQL connections at the wire protocol level.

Cordon can inject credentials into database connections, not just HTTP APIs. For PostgreSQL, cordon intercepts the wire protocol handshake and injects the password before the connection reaches the upstream database. Your application connects to a local port with no password and cordon handles authentication transparently.

## How it works

1. Cordon listens on a local port (e.g., `15432`)
2. Your app connects to `localhost:15432` with no password
3. Cordon intercepts the PostgreSQL authentication handshake
4. The real password, resolved when Cordon starts, is injected into the authentication handshake
5. The authenticated connection is forwarded to the upstream database

Your application never sees or handles database credentials.

<Note>
  PostgreSQL listeners currently resolve credentials at startup, not per connection. If you rotate the underlying secret, restart Cordon to pick up the new value.
</Note>

## Configuration

Database connections are configured as `[[listeners]]` entries in `cordon.toml`, alongside HTTP routes. See [Listeners](/configuration/listeners) for the full configuration reference.

```toml theme={null}
listen = 6790

[tls]
enabled = true
# Substitute paths from your real `cordon.toml` (typically under ~/.config/cordon/projects/<namespace>/certs/).
ca_cert_path = "/path/to/ca-cert.pem"
ca_key_path = "/path/to/ca-key.pem"

# ... your HTTP routes ...

[[listeners]]
name = "prod-db"
port = 15432
upstream = "db.prod.example.com:5432"
client_tls = "accept"

[listeners.auth]
type = "password"
username = "app_user"

[listeners.auth.secret]
source = "1password"
vault = "Engineering"
item = "Postgres Prod"
field = "password"
```

| Field           | Type    | Required | Description                                                                   |
| --------------- | ------- | -------- | ----------------------------------------------------------------------------- |
| `name`          | string  | Yes      | Identifier for the listener (used in logs)                                    |
| `port`          | integer | Yes      | Local port to listen on                                                       |
| `upstream`      | string  | Yes      | Upstream database host and port                                               |
| `client_tls`    | string  | No       | Client-to-Cordon TLS mode: `accept` (default), `require`, or `disable`.       |
| `auth.type`     | string  | Yes      | Auth type. Currently `password`.                                              |
| `auth.username` | string  | Yes      | Username to authenticate as                                                   |
| `auth.secret`   | object  | Yes      | Secret source reference. See [Secret Sources](/configuration/secret-sources). |

PostgreSQL listener upstreams are configured trust decisions in v1, matching configured HTTP credential routes. Cordon resolves the configured upstream once at listener startup and connects to the selected `SocketAddr`, which pins DNS and avoids a second lookup before connecting. Private, loopback, and link-local upstream policy will be revisited post-v1 in [#531](https://github.com/codezero-io/cordon/issues/531).

## Client TLS

PostgreSQL clients can request TLS from the local Cordon listener by sending a PostgreSQL `SSLRequest` before the startup packet. Configure `client_tls` on each listener:

| Mode      | Behavior                                                                                                                                                                                                                                 |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `accept`  | Default. Accepts TLS when Cordon can load valid `tls.ca_cert_path` and `tls.ca_key_path`; plaintext clients still work. If no usable CA is configured, Cordon declines SSL requests to preserve `sslmode=prefer` and plaintext behavior. |
| `require` | Requires clients to request and complete TLS before the PostgreSQL startup packet. This mode requires `tls.ca_cert_path` and `tls.ca_key_path`.                                                                                          |
| `disable` | Declines PostgreSQL SSL requests and continues only with clients that fall back to plaintext.                                                                                                                                            |

Cordon uses the same local CA configured under `[tls]` for PostgreSQL client TLS. Clients using `sslmode=verify-ca` or `sslmode=verify-full` must trust that CA, for example by running `cordon trust` or by configuring the client trust store directly.

Cordon presents a loopback certificate valid for `localhost`, `127.0.0.1`, and `::1`. For `sslmode=verify-full`, connect to the local listener with one of those host identities.

<Note>
  Cordon always requires TLS on the Cordon-to-upstream PostgreSQL leg. It sends a PostgreSQL `SSLRequest` to the upstream server and completes TLS before sending startup or authentication data. If the upstream declines TLS or certificate verification fails, Cordon fails closed and does not send credentials.
</Note>

## Connecting your application

Point your application at the local port instead of the upstream database:

<Tabs>
  <Tab title="Connection string">
    ```
    postgresql://app_user@localhost:15432/mydb
    ```

    No password in the connection string. Cordon injects it.
  </Tab>

  <Tab title="Environment variable">
    ```bash theme={null}
    DATABASE_URL=postgresql://app_user@localhost:15432/mydb
    ```
  </Tab>
</Tabs>

## Multiple databases

Configure multiple PostgreSQL listeners for different databases, each on its own local port:

```toml theme={null}
[[listeners]]
name = "prod-db"
port = 15432
upstream = "db.prod.example.com:5432"
client_tls = "accept"

[listeners.auth]
type = "password"
username = "app_user"

[listeners.auth.secret]
source = "1password"
vault = "Engineering"
item = "Postgres Prod"
field = "password"

[[listeners]]
name = "staging-db"
port = 15433
upstream = "db.staging.example.com:5432"
client_tls = "accept"

[listeners.auth]
type = "password"
username = "staging_user"

[listeners.auth.secret]
source = "keyring"
account = "staging-pg-pass"
```
