Skip to content

Multi-protocol guardian: SQL Server (TDS) frontend - #2

Draft
thecombinator wants to merge 6 commits into
capitec:mainfrom
thecombinator:multiprotocol-m1
Draft

Multi-protocol guardian: SQL Server (TDS) frontend#2
thecombinator wants to merge 6 commits into
capitec:mainfrom
thecombinator:multiprotocol-m1

Conversation

@thecombinator

Copy link
Copy Markdown
Collaborator

Summary

The proxy now fronts SQL Server as well as PostgreSQL: same binary, same decision pipeline, same governance semantics. A TDS client (sqlcmd, SSMS, .NET, ODBC, go-mssqldb) connects through the proxy; SQL batches are fingerprinted, cached, and judged by a SQL Server specialist agent with DMV and SHOWPLAN tools; rejections return as native SQL errors (Msg 50999) carrying the reason and a suggested fix. TDS 8.0 strict encryption is terminated at the frontend.

Design rationale and roadmap: docs/multiprotocol-design.md.

Commits (each independently reviewable)

  1. Literal redaction before LLM transmission + pinned EXPLAIN. Query literals never reach the model; EXPLAIN runs server-side against the trusted original. Also closes a model-directed-EXPLAIN vector. Benefits the existing PostgreSQL path immediately.
  2. Protocol-neutral pipeline extraction + wire.Frontend contract. The decision core (skip-list, cache, agent, mode, report) moves to internal/pipeline behind narrow interfaces. Zero behavior change; existing tests pass unmodified.
  3. TDS passthrough frontend. Packet-aware relay (MS-TDS framing).
  4. SQLBatch interception. Message assembly, UTF-16 SQL extraction, ERROR+DONE response encoding, T-SQL skip list, tsql agent persona.
  5. DMV/SHOWPLAN tools + direct Bedrock Converser. get_schema, get_indexes, get_table_stats, explain_query (SHOWPLAN_XML on a dedicated session).
  6. TDS 8.0 strict TLS termination. First-byte dispatch, ALPN tds/8.0, PRELOGIN encryption translated in both directions, LOGIN7 version presented to 7.x backends as 7.4.

Known limitations (deliberate v1 scope)

  • RPC (prepared/parameterized) and TransactionManager packets relay unjudged: ad-hoc and tool traffic is governed, driver traffic is not yet
  • No T-SQL masking (ScriptDom sidecar designed, not built)
  • Shunt mode falls back to blocking on TDS
  • Legacy 7.x client encryption (TLS-in-PRELOGIN) not terminated: strict or plaintext only
  • Test coverage is integration-led and below this repo's BDD/mutation bar

Verification

Full suite green and unmodified. Live integration tests against SQL Server (TDS_IT_BACKEND) cover passthrough, interception, rejection-with-fix, connection survival after rejection, and strict TLS end to end.

Review focus requested

The pipeline extraction (touches internal/proxy), and the TLS boundary code (tls.go, PRELOGIN/LOGIN7 rewrites) per SECURITY.md.

🤖 Generated with Claude Code

Leon Rajindrapersadh and others added 6 commits August 18, 2026 16:11
… original

Query literals can contain sensitive predicates (account numbers, ID
numbers, emails). The analysis agent only needs the query's shape, so:

- New internal/sqlnorm package: shared literal normalizer (string,
  dollar-quoted, and numeric literals; identifier-safe)
- PostgreSQL and Redshift agents send literal-redacted query text to
  Bedrock; a prompt note explains the redaction
- explain_query is pinned server-side to the trusted original query,
  so plan quality is unaffected and the model can no longer EXPLAIN
  arbitrary SQL
- Query fingerprinting reuses the same normalizer (one definition of
  query shape for cache keys and redaction)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ract

First structural step toward multi-protocol support (SQL Server/TDS
alongside PostgreSQL) per docs/multiprotocol-design.md:

- New internal/pipeline: the decision core (skip-list -> filter ->
  cache -> agent -> cache write -> report -> mode application),
  extracted verbatim from Proxy.analyzeQuery / analyzeQueryShunt,
  including reason-string suffixes, fail-open semantics, and
  best-effort cache/report handling. Narrow interfaces (Analyzer,
  ApprovalCache, Filter) keep frontends and tests decoupled from the
  full agent/cache surfaces.
- internal/proxy delegates both analyze functions to the pipeline;
  behavior unchanged, all existing tests green unmodified.
- New internal/wire: the Frontend contract that protocol frontends
  (pgwire today, TDS next) implement against the shared pipeline.
- Pipeline unit tests lock the behavioral contract: mode application,
  cache-hit suffixes, fail-open on agent error, shunt routing flags,
  report entries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SQL Server clients can now connect through the proxy: sqlcmd/SSMS ->
proxy -> SQL Server, verified against Azure SQL Edge with sqlcmd.

- internal/wire/tds: TDS packet framing (8-byte headers, EOM status,
  type constants per MS-TDS) with defensive length limits, plus a
  packet-aware passthrough server: client->backend relays whole TDS
  packets (establishing the message-boundary layer that milestone 3
  interception builds on), backend->client streams raw bytes.
- cmd/pgproxy: optional TDS listener via TDS_LISTEN_ADDR and
  TDS_BACKEND_ADDR; the binary can now front PostgreSQL and SQL Server
  simultaneously.
- Encryption: PRELOGIN is relayed untouched, so peers negotiate
  directly; this milestone targets unencrypted dev loops (sqlcmd -N
  disable). TLS termination (TDS 8.0 strict encryption preferred) is
  scheduled with interception.

Observed handshake through the proxy: Prelogin -> Login7 -> SQLBatch,
each surfaced with type, length, and EOM: the interception points for
milestone 3 are already visible in the packet log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SQL Server clients now get the same governance as PostgreSQL clients:
rejected batches never reach the backend and the client receives a
native ERROR token with the reason and the suggested fix.

- internal/wire/tds: message assembly (multi-packet until EOM),
  SQLBatch SQL extraction (ALL_HEADERS + UTF-16LE per MS-TDS),
  ERROR+DONE response encoding (error 50999, severity 16) rendered by
  sqlcmd/SSMS/drivers, fail-open on parse anomalies, and a T-SQL skip
  list for session/tooling chatter.
- internal/agent: TSQLAgent, a SQL Server engine-specialist persona
  judging on query shape alone (conservative by design until DMV and
  SHOWPLAN tools land); literals redacted before transmission like the
  other dialects.
- internal/sqlnorm: shared shape Fingerprint for dialects without a
  native parser-based normalizer; T-SQL verdicts share the approval
  cache.
- cmd/pgproxy: TDS frontend wires a tsql pipeline when analysis is
  enabled; shunt mode falls back to blocking on TDS for now.
- Live integration test (TDS_IT_BACKEND) drives go-mssqldb through the
  frontend against a real backend: approved flows, rejection carries
  reason+fix, connection survives rejection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…one 4)

The tsql persona now reasons like a DBA instead of judging query text
alone:

- internal/agent/tools_tsql.go: read-only SQL Server tools over
  go-mssqldb: get_schema (sys.columns), get_indexes (sys.indexes +
  dm_db_index_usage_stats), get_table_stats (dm_db_partition_stats),
  and explain_query via SHOWPLAN_XML on a dedicated session (SHOWPLAN
  is session-sticky; never runs on a pooled connection). explain is
  pinned to the trusted original query; the model only ever sees the
  literal-redacted shape.
- internal/agent/bedrock.go: direct Bedrock Converse client restored
  as a Converser implementation (temperature 0, MaxTokens 4096),
  for deployments that call Bedrock without the Portkey gateway.
- TSQLAgent executes tool calls in its agentic loop; prompt upgraded
  from shape-only judging to a methodical tools-first process with a
  conservative fallback when tools fail.
- cmd/pgproxy: TDS analysis uses Bedrock directly; TDS_BACKEND_DSN
  enables the DMV/SHOWPLAN toolset.
- Live integration test (TSQL_IT_DSN) verifies all four tools against
  a real backend, ShowPlanXML included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Modern Microsoft clients (SSMS 20+, .NET SqlClient 5+, ODBC 18+,
go-mssqldb encrypt=strict) encrypt by default and cannot be talked
down to plaintext. The frontend now terminates TDS 8.0 strict
encryption, which is ordinary TLS from the first byte:

- First-byte dispatch: a TLS ClientHello (0x16) enters strict
  termination (ALPN tds/8.0, TLS 1.2+); a TDS packet type enters the
  legacy 7.x path unchanged.
- Boundary translation for 7.x backends: the PRELOGIN ENCRYPTION
  option is rewritten in both directions (backend told NOT_SUP, the
  tunnel is the encryption; client told STRICT), and the LOGIN7
  TDSVersion field is presented to the backend as 7.4 (a strict
  client stamps 8.0, which a 7.x engine refuses).
- Certificates via TDS_TLS_CERT_PATH/TDS_TLS_KEY_PATH, or
  TDS_TLS_SELF_SIGNED=true for dev loops. Note: strict mode mandates
  certificate validation per spec (trustservercertificate is
  ignored); the integration test pins the cert like a real client.
- Interception is unchanged inside the tunnel: the live strict test
  verifies approved flow and guardian rejection over TLS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant