Skip to content

feat(cli): connection flags + DX (quiet logging, random port, best-effort TLS)#397

Open
coderdan wants to merge 5 commits into
mainfrom
dan/cli-connection-flags
Open

feat(cli): connection flags + DX (quiet logging, random port, best-effort TLS)#397
coderdan wants to merge 5 commits into
mainfrom
dan/cli-connection-flags

Conversation

@coderdan
Copy link
Copy Markdown
Contributor

@coderdan coderdan commented May 30, 2026

Makes the proxy usable as a plain CLI (e.g. npx stash proxy) without hand-setting env vars. Two commits.

1. Connection flags + bug fix

  • --database-url <URL> — parse a full postgres://user:pass@host:port/db (via tokio-postgres' parser); individual --db-* flags override.
  • --db-port (-P), --db-password (-W) — the missing scalar flags.
  • Bug: --db-user set CS_DATABASE__USER but the field is username (CS_DATABASE__USERNAME) — silent no-op; now fixed.

2. CLI DX

  • Quiet by default — logs errors only unless --debug, an explicit --log-level/CS_LOG__LEVEL, or a config file is present (production via env/config unchanged). Listen address always prints.
  • Random-port fallback — if the listen port is the default (no CS_SERVER__PORT, no config file) and is in use, bind an OS-assigned port and report it instead of failing. Explicit ports are respected.
  • Best-effort inbound TLS — a TLS misconfig falls back to a plaintext listener with a notice instead of exiting. --tls forces TLS (fatal if it can't); --no-tls forces plaintext.

Tests / validation

  • Regression tests: database_url_sets_connection_env, bind_falls_back_to_random_port_when_default_in_use.
  • cargo fmt + clippy --all-features -D warnings clean.
  • Validated end-to-end locally: npx stash proxy --database-url postgres://…/db connects, auto-falls-back from a broken/inherited TLS config to plaintext, and (since the default port was busy) listens on an OS-assigned port — no flags or env munging.

Notes

  • Surfaced while building the npx stash proxy distribution prototype (separate branch), but stands alone as proxy CLI improvements.
  • Possible follow-up: No Encrypt configuration table is logged at ERROR (shows even when quiet) when connecting to a DB without EQL — arguably warn/info.

Summary by CodeRabbit

  • New Features

    • Added CLI options: --password, --no-tls, --tls, and --debug
    • Command-line database URL now populates connection fields automatically
  • Behavior Changes / Bug Fixes

    • If port is in use and not explicitly set, proxy falls back to an OS-assigned port
    • Logging defaults to error-only unless debug or explicit level set
    • TLS validation may downgrade to plaintext when TLS isn't forced
    • Missing encrypt-config now logs a warning
    • Database error logging reduced to debug-level
  • Tests

    • Added tests covering database URL parsing, non-UTF8 password handling, bind fallback, and error parsing behavior

…db-user

Makes the proxy usable as a plain CLI (e.g. `npx stash proxy`) without
hand-setting env vars:

- --database-url: parse a full postgres:// connection string (host, port,
  user, password, dbname) via tokio-postgres' parser; individual flags override.
- --db-port / --db-password: the missing scalar connection flags.
- --no-tls: listen for client connections in plaintext, overriding any CS_TLS__*
  env/config (inbound only; does not affect the proxy->database connection).
  Avoids a hard startup error when a stray CS_TLS__* points at a missing cert.

Fixes a bug: --db-user set CS_DATABASE__USER, but the config field is
`username` (CS_DATABASE__USERNAME), so --db-user was silently a no-op.

Adds a regression test for the URL parsing + the USERNAME fix. Validated
locally: `npx stash proxy --database-url postgres://.../db --no-tls` connects
and listens.
Copilot AI review requested due to automatic review settings May 30, 2026 00:48
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7d86b056-bb43-4756-a278-754fbd8d2604

📥 Commits

Reviewing files that changed from the base of the PR and between e13f5b8 and c928bdf.

📒 Files selected for processing (2)
  • packages/cipherstash-proxy-integration/src/migrate/mod.rs
  • packages/cipherstash-proxy/src/config/tandem.rs

📝 Walkthrough

Walkthrough

Adds CLI DB and TLS flags; parses PostgreSQL URLs into CS_DATABASE__* environment variables with CLI overrides; honors --no-tls; enables bind fallback to an OS-assigned port when appropriate; threads a force-TLS flag through init and reloads; lowers some backend error logging; updates related tests.

Changes

CLI, config, connect, and init

Layer / File(s) Summary
CLI argument extensions
packages/cipherstash-proxy/src/cli/mod.rs
Added db_password: Option<String>, no_tls: bool, tls: bool, and debug: bool; refined database_url, db_host, and db_port clap metadata and help text.
Database URL parsing and env overrides
packages/cipherstash-proxy/src/config/tandem.rs
Added apply_database_url to parse Postgres URLs into CS_DATABASE__HOST, CS_DATABASE__PORT, CS_DATABASE__USERNAME, CS_DATABASE__PASSWORD, and CS_DATABASE__NAME; TandemConfig::build applies the URL then CLI overrides (including db_port and db_password); load honors --no-tls; includes unit tests validating env vars and non-UTF8 password rejection.
Listener bind retry and reporting
packages/cipherstash-proxy/src/connect/mod.rs
bind_with_retry gains allow_random_fallback to bind to host:0 when AddrInUse on implicit/default ports; adds report_listening helper and a test verifying fallback behavior.
Init, TLS validation, logging, and reload wiring
packages/cipherstash-proxy/src/main.rs
init signature gains force_tls: bool; logging defaults to error-only unless debug/loglevel/config present; TLS validated up-front and treated as fatal only when forced, else downgraded to plaintext; bind fallback enabled only when port not explicitly set; reloads pass args.tls.
Encrypt config logging change
packages/cipherstash-proxy/src/proxy/encrypt_config/manager.rs
Missing EncryptConfig table case now logs warn! instead of error! while continuing with an empty configuration.
Postgres backend logging tweaks
packages/cipherstash-proxy/src/postgresql/backend.rs
Imports ErrorResponseCode, trims tracing imports, and reduces forwarded DB error logging to debug! with extracted code/message.
ErrorResponse accessor and tests
packages/cipherstash-proxy/src/postgresql/messages/error_response.rs
Adds pub fn field(&self, code: ErrorResponseCode) -> Option<&str> and a unit test asserting returned values for Code/Message and None for missing Detail.
Integration test Args update
packages/cipherstash-proxy-integration/src/migrate/mod.rs
Updates test to initialize new Args fields (database_url, db_port, db_password, no_tls, tls, debug) to None/false to match CLI changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • tobyhede
  • freshtonic

Poem

🐰 I hop through flags and envs with care,
I parse the URL and set each pair,
If TLS says no, I gently fall back,
Ports that collide find a new track,
I munch on logs while tests stay fair.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding CLI connection flags (--database-url, --db-password, --db-port), improving quiet logging by default, implementing random port fallback, and enabling best-effort TLS configuration.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dan/cli-connection-flags

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds CLI-only database connection configuration and inbound TLS override support for cipherstash-proxy, making the binary easier to run without preconfigured environment variables.

Changes:

  • Adds --database-url, --db-port, --db-password, and --no-tls CLI flags.
  • Applies database URL values into CS_DATABASE__* env vars before layered config loading.
  • Fixes --db-user to set CS_DATABASE__USERNAME instead of the unused CS_DATABASE__USER.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/cipherstash-proxy/src/cli/mod.rs Adds the new CLI arguments and help text.
packages/cipherstash-proxy/src/config/tandem.rs Applies CLI database overrides, disables inbound TLS when requested, and adds regression coverage for URL parsing/user env var behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/cipherstash-proxy/src/config/tandem.rs Outdated
Comment on lines +423 to +427
if let Some(password) = pg.get_password() {
if let Ok(password) = std::str::from_utf8(password) {
env::set_var("CS_DATABASE__PASSWORD", password);
}
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cipherstash-proxy/src/config/tandem.rs (1)

395-398: ⚡ Quick win

Doc comment contains stale text from another function.

Lines 395-396 ("Extracts a field name (if present)..." and "This is called in build...") describe extract_missing_field_and_key below, not apply_database_url. This appears to be left over from when the new function was inserted.

📝 Proposed fix
-/// Extracts a field name (if present) from a config::ConfigError string
-/// This is called in `build` if a ConfigError message contains the string `missing field`
 /// Parse a PostgreSQL connection string (e.g.
 /// "postgres://user:pass@host:5432/dbname") and set the corresponding
 /// CS_DATABASE__* env vars for each component present. Individual --db-* flags
 /// are applied after this and take precedence.
 fn apply_database_url(url: &str) -> Result<(), Error> {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cipherstash-proxy/src/config/tandem.rs` around lines 395 - 398, The
doc comment above apply_database_url is stale and describes
extract_missing_field_and_key instead; update the comment for apply_database_url
to accurately describe that it parses a PostgreSQL connection string (e.g.
"postgres://user:pass@host:5432/dbname"), sets the CS_DATABASE__* environment
variables for components present, and notes that individual --db-* flags
(applied later) take precedence; remove or relocate the two sentences about
extracting field names and the build call so they belong with
extract_missing_field_and_key (or delete them if redundant).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cipherstash-proxy/src/config/tandem.rs`:
- Around line 423-427: The code currently drops non-UTF8 passwords silently;
update the pg.get_password() branch to log a warning when
std::str::from_utf8(password) returns Err instead of silently skipping—use your
project's logger (e.g., tracing::warn! or log::warn!) to emit a clear message
that the password contains non-UTF8 bytes and CS_DATABASE__PASSWORD was not set,
and include a safe representation of the raw bytes (hex or base64) for
debugging; keep the existing env::set_var("CS_DATABASE__PASSWORD", password)
path unchanged when from_utf8 succeeds.

---

Nitpick comments:
In `@packages/cipherstash-proxy/src/config/tandem.rs`:
- Around line 395-398: The doc comment above apply_database_url is stale and
describes extract_missing_field_and_key instead; update the comment for
apply_database_url to accurately describe that it parses a PostgreSQL connection
string (e.g. "postgres://user:pass@host:5432/dbname"), sets the CS_DATABASE__*
environment variables for components present, and notes that individual --db-*
flags (applied later) take precedence; remove or relocate the two sentences
about extracting field names and the build call so they belong with
extract_missing_field_and_key (or delete them if redundant).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 240c19cb-ffdf-4e10-b6ca-9aea6530acf7

📥 Commits

Reviewing files that changed from the base of the PR and between 100829b and 442c2cd.

📒 Files selected for processing (2)
  • packages/cipherstash-proxy/src/cli/mod.rs
  • packages/cipherstash-proxy/src/config/tandem.rs

Comment thread packages/cipherstash-proxy/src/config/tandem.rs
…t TLS

DX improvements for running proxy as a CLI (e.g. npx stash proxy):

- Quiet by default: log errors only unless --debug, an explicit --log-level /
  CS_LOG__LEVEL, or a config file is present (production/observability via env
  or config is unchanged). The listen address always prints.
- Random-port fallback: if the listen port is the default (no CS_SERVER__PORT,
  no config file) and is already in use, bind an OS-assigned port and report it
  instead of failing. An explicitly configured port is still respected.
- Best-effort inbound TLS: a TLS misconfiguration now falls back to a plaintext
  listener with a notice instead of exiting. --tls forces TLS (fatal if it
  can't be set up); --no-tls forces plaintext.

Adds a regression test for the random-port fallback.

Relates to BUG-300-adjacent CLI usability work.
@coderdan coderdan changed the title fix(cli): add --database-url/--db-port/--db-password/--no-tls, fix --db-user feat(cli): connection flags + DX (quiet logging, random port, best-effort TLS) May 30, 2026
When a target database has no eql_v2_configuration table, the proxy continues
in passthrough -- it's only an error if you actually want encryption. Logging it
at error made it show even with the new quiet-by-default logging; warn is the
right level.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cipherstash-proxy/src/connect/mod.rs`:
- Around line 67-82: The primary and fallback bind paths both format host:port
with naive string interpolation which breaks on IPv6 literals; update the code
so both TcpListener::bind calls use a proper socket-address builder or
consistently bracket IPv6 hosts (e.g. construct a SocketAddr via
ServerConfig::to_socket_address() or add logic that wraps an IPv6 literal in [ ]
before combining with port) so that the same bracket-handling used for the
primary path is applied to the fallback path (references: TcpListener::bind
calls and ServerConfig::to_socket_address()).

In `@packages/cipherstash-proxy/src/main.rs`:
- Around line 233-240: The reload logic rejects SIGHUP reloads because init may
have downgraded the in-memory TandemConfig (clearing config.tls and setting
config.server.require_tls = false) while a freshly loaded new_config still shows
TLS set, so has_network_config_changed sees a diff and forces a restart; fix by
normalizing both configs to the same effective/network representation before
comparing — e.g., extract the TLS-downgrade/normalization performed in init into
a helper (or call the same downgrade logic) and apply it to new_config inside
reload_application_config (before has_network_config_changed), or change
has_network_config_changed to compare “effective” TLS state instead of raw
config.tls/config.server.require_tls; target symbols: init, TandemConfig::load,
reload_application_config, has_network_config_changed, Proxy { config } and the
fields TandemConfig.tls and TandemConfig.server.require_tls.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2214fe66-11bb-48f0-ae08-9fb700a83c51

📥 Commits

Reviewing files that changed from the base of the PR and between 442c2cd and e1c5fd6.

📒 Files selected for processing (4)
  • packages/cipherstash-proxy/src/cli/mod.rs
  • packages/cipherstash-proxy/src/config/tandem.rs
  • packages/cipherstash-proxy/src/connect/mod.rs
  • packages/cipherstash-proxy/src/main.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cipherstash-proxy/src/config/tandem.rs

Comment on lines +67 to +82
Err(err) if err.kind() == std::io::ErrorKind::AddrInUse && allow_random_fallback => {
match TcpListener::bind(format!("{}:0", server.host)).await {
Ok(listener) => {
println!(
"Port {} is already in use; listening on an OS-assigned port instead.",
server.port
);
report_listening(&listener);
return listener;
}
Err(err) => {
error!(msg = "Error binding connection", error = err.to_string());
std::process::exit(exitcode::CONFIG);
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how ServerConfig::to_socket_address formats host:port (brackets for IPv6?)
ast-grep --pattern $'fn to_socket_address($$$) {
  $$$
}'
echo "----- raw search -----"
rg -nP -C3 '\bto_socket_address\b'

Repository: cipherstash/proxy

Length of output: 2830


Fix IPv6-literal concern: primary bind has the same formatting issue as the fallback

The fallback uses format!("{}:0", server.host), but ServerConfig::to_socket_address() (used by the retry/primary path) is also format!("{}:{}", self.host, self.port) with no IPv6-literal bracket handling. So the “primary presumably handles this” distinction doesn’t hold; if IPv6 literals are supported, both paths need consistent bracketed formatting (or a socket-address builder that handles IPv6 correctly).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cipherstash-proxy/src/connect/mod.rs` around lines 67 - 82, The
primary and fallback bind paths both format host:port with naive string
interpolation which breaks on IPv6 literals; update the code so both
TcpListener::bind calls use a proper socket-address builder or consistently
bracket IPv6 hosts (e.g. construct a SocketAddr via
ServerConfig::to_socket_address() or add logic that wraps an IPv6 literal in [ ]
before combining with port) so that the same bracket-handling used for the
primary path is applied to the fallback path (references: TcpListener::bind
calls and ServerConfig::to_socket_address()).

Comment on lines +233 to +240
(Some(_), Some(err), false) => {
eprintln!(
"TLS configuration is invalid ({err}); falling back to a plaintext listener. \
Pass --tls to make this fatal, or --no-tls to silence this warning."
);
config.tls = None;
config.server.require_tls = false;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm has_network_config_changed compares tls/require_tls and is used on reload
rg -nP -C3 '\bhas_network_config_changed\b'
# Confirm reload uses the live (possibly downgraded) proxy.config
rg -nP -C3 'reload_application_config\s*\('

Repository: cipherstash/proxy

Length of output: 2326


🏁 Script executed:

#!/bin/bash
# Inspect TLS validation/downgrade logic around the reported lines
sed -n '200,260p' packages/cipherstash-proxy/src/main.rs

# Inspect reload + change-detection logic around has_network_config_changed usage
sed -n '280,360p' packages/cipherstash-proxy/src/main.rs

# Find where require_tls is set/cleared during init
rg -n --context 2 'require_tls' packages/cipherstash-proxy/src/main.rs

Repository: cipherstash/proxy

Length of output: 4552


🏁 Script executed:

#!/bin/bash
# Confirm SIGHUP handler passes &proxy.config into reload_application_config
sed -n '60,130p' packages/cipherstash-proxy/src/main.rs

# Locate Proxy struct definition and how it stores config (look for `config:` field)
rg -n "struct Proxy\b" packages/cipherstash-proxy/src -S
rg -n "\bconfig:\s*TandemConfig\b|\bconfig:\s*.*TandemConfig\b|proxy\.config\b" packages/cipherstash-proxy/src -S

# Locate Proxy::init to see what gets stored on success
rg -n "impl Proxy\b|fn init\(config:|Proxy::init" packages/cipherstash-proxy/src -S

Repository: cipherstash/proxy

Length of output: 7142


🏁 Script executed:

#!/bin/bash
# Check whether TandemConfig::load validates TLS (would affect whether new_config.tls stays Some)
rg -n "fn load\\b|impl .*TandemConfig" packages/cipherstash-proxy/src/config/tandem.rs
sed -n '1,260p' packages/cipherstash-proxy/src/config/tandem.rs
rg -n "check_cert|check_private_key|configure_server|tls::configure" packages/cipherstash-proxy/src/config/tandem.rs

Repository: cipherstash/proxy

Length of output: 9703


🏁 Script executed:

#!/bin/bash
# Locate ServerConfig definition + defaults for require_tls
rg -n "struct ServerConfig|require_tls" packages/cipherstash-proxy/src/config -S

# Show the portion that defines ServerConfig fields/defaults and how require_tls is derived
fd -e rs packages/cipherstash-proxy/src/config -a | head -n 20 >/dev/null 2>&1 || true
# Print candidate server config files around require_tls
rg -n "require_tls" packages/cipherstash-proxy/src/config -S --files-with-matches

Repository: cipherstash/proxy

Length of output: 966


🏁 Script executed:

#!/bin/bash
set -e

# Show ServerConfig struct + default for require_tls
sed -n '1,120p' packages/cipherstash-proxy/src/config/server.rs

# Find all assignments to require_tls during config build/load
rg -n "require_tls\s*=" packages/cipherstash-proxy/src/config -S
rg -n "require_tls" packages/cipherstash-proxy/src -S

# Inspect any TLS parsing/config wiring in tandem.rs (where tls Some(...) might imply require_tls)
rg -n "tls" packages/cipherstash-proxy/src/config/tandem.rs -S --context 2

Repository: cipherstash/proxy

Length of output: 5403


Non-fatal TLS downgrade can prevent SIGHUP reloads indefinitely (until TLS is removed/disabled or the process restarts).

        (Some(_), Some(err), false) => {
            eprintln!(
                "TLS configuration is invalid ({err}); falling back to a plaintext listener. \
                 Pass --tls to make this fatal, or --no-tls to silence this warning."
            );
            config.tls = None;
            config.server.require_tls = false;
        }

When inbound TLS is configured but invalid and --tls is not set, init downgrades in place by clearing config.tls and setting config.server.require_tls = false. That downgraded TandemConfig is what the running proxy stores (Proxy { pub config: Arc<TandemConfig> }). On SIGHUP, reload_application_config(&proxy.config, &args) loads a fresh new_config via TandemConfig::load(args) (which only disables TLS for --no-tls, and does not validate/downgrade invalid certs) and then rejects the reload before calling init if has_network_config_changed(config, &new_config) reports a change. Since has_network_config_changed compares both current.tls and current.server.require_tls, the downgraded current (tls = None) differs from new_config (tls = Some), so reload returns NetworkConfigurationChangeRequiresRestart and the SIGHUP handler keeps the old proxy (unwrap_or(proxy)).

Adjust the reload change detection to compare effective (post-downgrade/normalized) network config, or apply the same downgrade logic to new_config before calling has_network_config_changed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cipherstash-proxy/src/main.rs` around lines 233 - 240, The reload
logic rejects SIGHUP reloads because init may have downgraded the in-memory
TandemConfig (clearing config.tls and setting config.server.require_tls = false)
while a freshly loaded new_config still shows TLS set, so
has_network_config_changed sees a diff and forces a restart; fix by normalizing
both configs to the same effective/network representation before comparing —
e.g., extract the TLS-downgrade/normalization performed in init into a helper
(or call the same downgrade logic) and apply it to new_config inside
reload_application_config (before has_network_config_changed), or change
has_network_config_changed to compare “effective” TLS state instead of raw
config.tls/config.server.require_tls; target symbols: init, TandemConfig::load,
reload_application_config, has_network_config_changed, Proxy { config } and the
fields TandemConfig.tls and TandemConfig.server.require_tls.

A PostgreSQL error forwarded to the client is normal proxy operation -- the
client already surfaces it -- but the backend logged every one at ERROR level as
the full Debug-formatted ErrorResponse struct. That is unreadable and, with
quiet-by-default logging, was the only thing showing on a failed query (and it
bled into --psql sessions).

Log it at debug instead, concisely (SQLSTATE code + message) via a new
ErrorResponse::field() accessor. Nothing on a SQL error by default; a clean
one-line entry under --debug. Adds a unit test for the accessor.
- Add the six new Args fields (database_url, db_port, db_password, no_tls,
  tls, debug) to the struct literal in the migrate integration test. The
  proxy crate compiled, but the integration crate's literal was stale, so
  the full-workspace test build failed E0063 — this is the CI failure.
- Reject a non-UTF8 password from --database-url with a clear
  InvalidParameter error instead of silently dropping it (which would
  connect with no/stale credentials). Add a regression test; the branch is
  reachable because tokio-postgres stores the URL password as raw bytes
  without UTF-8 validation.
- Remove a stale doc comment left over from another function above
  apply_database_url.
@coderdan
Copy link
Copy Markdown
Contributor Author

coderdan commented Jun 1, 2026

Review feedback addressed (pushed in c928bdf)

Thanks for the reviews. Status:

Fixed in this PR

  • CI failure (the red Test jobs): root cause was a compile error, not test logic — the new Args fields weren't added to the struct literal in cipherstash-proxy-integration/src/migrate/mod.rs, so the integration crate failed to build (E0063). Added the six missing fields.
  • Non-UTF8 password (Copilot + CodeRabbit, tandem.rs): now returns a clear InvalidParameter error instead of silently dropping the password (which would connect with no/stale credentials). Went with Copilot's error-out approach over warn-and-continue, since the password is required downstream. Added a regression test (database_url_rejects_non_utf8_password) — the branch is reachable because tokio-postgres stores the URL password as raw bytes without UTF-8 validation.
  • Stale doc comment (Copilot + CodeRabbit, tandem.rs): removed the leftover extract_missing_field_and_key text above apply_database_url.

Deferred to follow-up issues (real, but pre-existing / behavioural and out of scope for this CLI-feature PR)

Local validation: cargo fmt --all --check, cargo clippy --all --all-features --tests -- -D warnings, and the affected unit tests all pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants