Skip to content

Quickstart

This tutorial is the first task for an evaluator who already has access to a deployment, or for an operator installing gdsgate on-premise. Its target result is an evidence bundle containing a trusted client configuration, an allowed PostgreSQL query, a direct-backend negative, a policy denial, a matching authorization row, and an audit-chain check from one disposable split lab.

There is no public download. The deployment administrator supplies the client, release verification material, and a version-matched copy of the canonical testbed/ bundle. An on-premise operator supplies the same files from the installation they control. Run each block on the named role host and carry only the named non-secret inputs between hosts.

Acquisition boundary

Do not invent a public URL or copy a client from an unrelated deployment. If the deployment administrator cannot supply the versioned client, verification material, and matching lab bundle, stop. Public documentation describes gdsgate and does not document the hosted service's user interface.

Outcome and boundary

Reader and outcome: An evaluator with an administrator-supplied client and lab bundle confirms that one authenticated client can reach an allowed resource through Proxy and Connector, while an unapproved resource and a direct backend route fail for different reasons.

Actors and permissions: The lab administrator owns the Authority, Proxy, Connector, disposable PostgreSQL state, policy, node identities, and cleanup. The evaluator acts as alice@example.test. This is a reserved fixture name, not a real account.

Topology: Use the split topology for the acceptance result. The Client reaches Proxy. Proxy consults Authority and carries the authorized data path to Connector. Connector reaches the backend from the protected side.

flowchart LR
  client["Client<br/>alice@example.test"] -->|client TLS| proxy["Proxy<br/>proxy-1"]
  proxy -->|control and policy| authority["Authority<br/>authority-1"]
  connector["Connector<br/>connector-1"] -.->|outbound tunnel| proxy
  connector -->|protected connection| db[("PostgreSQL<br/>lab-postgres")]
  idp[Approved identity provider] -.->|identity token| client
  idp -.->|issuer and keys| authority

The fixture port conventions are authority-1:50051 for Authority mTLS, authority-1:50050 for bootstrap enrollment, proxy-1:50061 for the public Proxy listener, proxy-1:50062 for the Connector tunnel, and lab-postgres:5432 for the backend. These values come from the shared example contract and testbed. They are not a supported production endpoint matrix.

Fixture Host role Owner and lifecycle
authority-1 Authority control plane, policy, catalog, and audit store Lab administrator; start first and preserve state during the run
proxy-1 Client-facing Proxy and Connector tunnel endpoint Lab administrator; enroll once and retain transport state
connector-1 Protected-side Connector Lab administrator; enroll once and retain transport state
lab-postgres Disposable PostgreSQL backend seeded with three widgets rows Lab administrator; never use production data
lab-read Allowed catalog resource backed by the disposable database Lab administrator; policy grants alice@example.test access
lab-denied Nearest denied catalog resource Lab administrator; Connector may serve it, but policy denies the evaluator

Positive verification: The native psql query returns 3, and the matching audit row records the authorization decision.

Nearest negative: The evaluator's direct psql attempt to the private backend fails at the network boundary. The separate lab-denied attempt fails at gateway authorization. Neither failure is interchangeable evidence.

Failure and recovery: If the handoff is incomplete, the host is not the supported Linux target, or the disposable identity fixture is missing, stop and return the missing item to the deployment administrator. Do not substitute an unverified binary or a real identity.

Security consequence: A successful gateway query proves an authorized data path, not host isolation. The direct-backend check is required to measure the network boundary.

Cleanup: Preserve the evidence needed for review, revoke temporary node identities and access, stop processes, and remove only the disposable lab root.

Next: Complete Acquire and verify before preparing the split lab.

Acquire and verify

Reader and outcome: The evaluator obtains an approved binary and verifies its release integrity before execution.

Prerequisites: The supported target is x86_64-unknown-linux-musl: Linux on x86-64, statically linked with musl. The gdsgate binary therefore has no system runtime library to install. The lab still requires a POSIX shell, curl, psql, sha256sum, the administrator-selected signature verifier, sufficient disk for the disposable services, and permission to create the isolated lab network. A second operating system or architecture is not supported by this contract.

Administrator handoff

There is no public artifact URL. Obtain the client from the administrator of the deployment you can access, or from the on-premise installation you operate. The handoff must include the release directory and the testbed/ bundle from the same version. If those versions differ, stop.

Input: Set the variables from the administrator handoff and run the supplied signature-verification procedure before the checksum check.

: "${RELEASE_DIR:?Set RELEASE_DIR to the approved release directory}"
: "${RELEASE_TAG:?Set RELEASE_TAG to the approved release tag}"
LAB_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/gdsgate-eval.XXXXXX")"
chmod 700 "$LAB_ROOT"
touch "$LAB_ROOT/.gdsgate-evaluator-lab"
test -r "$RELEASE_DIR/SHA256SUMS"
test -r "$RELEASE_DIR/SHA256SUMS.sig"
# Run the release owner's approved signature verifier for SHA256SUMS.sig first.
(cd "$RELEASE_DIR" && sha256sum -c SHA256SUMS)
install -m 0755 \
  "$RELEASE_DIR/gdsgate-$RELEASE_TAG-x86_64-unknown-linux-musl" \
  "$LAB_ROOT/gdsgate"
"$LAB_ROOT/gdsgate" --version

Expected result: The signature verifier accepts the checksum manifest, sha256sum prints OK for the selected artifact, and gdsgate --version prints the installed version. Version text is release-specific.

Positive verification: Keep the signed manifest and checksum output with the lab evidence. Confirm that the binary path, release tag, target tuple and reported version agree.

Nearest negative: A missing file, signature failure, checksum mismatch, or unsupported target is a stop condition. Do not run the binary after a failed integrity check.

Failure and recovery: Return the exact failing filename, target tuple and command exit status to the release owner. Do not substitute a source checkout, an unverified build, or a different platform.

Security consequence: The checksum is useful only after the signature and trust root are verified. A checksum from an untrusted directory is not release identity.

Cleanup: The installed Client binary remains inside this host's LAB_ROOT and is removed with that disposable root after the run. The approved bundle must create a separate marked LAB_ROOT on each service host; a path or process ID from one host is never reused as though it belonged to another.

Next: Prepare the split lab.

Prepare the split lab

Reader and outcome: The lab administrator creates a private, disposable workspace and names the exact configuration and evidence files used in the run.

Prerequisites: Use the administrator-supplied testbed/ bundle from the same release as the verified binary. Its canonical split fixture contains the disposable OIDC realm, seeded PostgreSQL data, lab-read and lab-denied resources, anchored audit configuration, and role launch assets. Use an isolated network containing only authority-1, proxy-1, connector-1, and lab-postgres. The Client must not have a route to lab-postgres:5432. The approved bundle must distribute the verified binary to each role host and include the three role configuration files, an explicit per-host launcher, local readiness URLs, and a policy fixture with a persistent Authority store, configured audit anchor, and approved identity provider.

Input: Run this preparation on authority-1. Repeat the file-presence check for the binary and owned configuration on proxy-1 and connector-1 through the bundle's launcher. The directories named by the variables are supplied by the lab administrator; they are not generated public defaults.

umask 077
LAB_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/gdsgate-eval.XXXXXX")"
chmod 700 "$LAB_ROOT"
touch "$LAB_ROOT/.gdsgate-evaluator-lab"
mkdir -p "$LAB_ROOT/evidence"
: "${GDSGATE_BIN:=$LAB_ROOT/gdsgate}"
: "${AUTHORITY_CONFIG:?Set AUTHORITY_CONFIG to the approved authority fixture}"
: "${POLICY_FILE:?Set POLICY_FILE to the approved evaluator policy}"
for file in "$GDSGATE_BIN" "$AUTHORITY_CONFIG" "$POLICY_FILE"; do
  test -r "$file"
done
"$GDSGATE_BIN" authority policy validate "$POLICY_FILE"

Expected result: The policy validator exits successfully. The three role files resolve to the split topology, and the lab administrator can show that the Authority store is persistent rather than sqlite::memory:. A persistent store is required because enrollment, catalog, policy, and audit state must survive process restart during the run. bootstrap-admin writes a disposable administrative identity, and the Authority process then runs with that config.

Positive verification: Bootstrap the disposable administrator, run gdsgate doctor --json for authority-1, start Authority, and require its local /readyz endpoint to return success. A fresh Proxy or Connector has no enrolled transport identity yet, so its pre-enrollment doctor report correctly exits 2; run those two checks after enrollment instead of calling that expected absence a configuration failure.

"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority bootstrap-admin \
  --output-dir "$LAB_ROOT/admin-id" --ttl 300
export GDSGATE_ADMIN_IDENTITY_DIR="$LAB_ROOT/admin-id"
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" doctor --json \
  > "$LAB_ROOT/evidence/authority-doctor.json"
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority \
  > "$LAB_ROOT/evidence/authority.log" 2>&1 &
AUTHORITY_PID=$!
: "${AUTHORITY_READY_URL:?Set the authority-1 local /readyz URL from the approved fixture}"
for attempt in $(seq 1 30); do
  curl -fsS "$AUTHORITY_READY_URL" > /dev/null && break
  sleep 1
done
curl -fsS "$AUTHORITY_READY_URL" > "$LAB_ROOT/evidence/authority-ready.txt"

Nearest negative: If the Authority doctor invocation exits 2, do not start Authority. For Proxy and Connector, an exit 2 remains a stop condition after their transport identity has been enrolled. The JSON identifies the invalid or missing setting. If the backend is routable from the Client, stop because the split topology is not isolated.

Failure and recovery: Correct the named configuration in the fixture owner scope, rerun policy validation and the affected doctor check, and retain both the failing and corrected evidence.

Security consequence: doctor checks configuration shape. It does not prove identity, policy behavior, backend isolation, or release provenance.

Cleanup: Keep the lab root mode 0700, keep identity directories and keys inside it, and remove it only after temporary access has been revoked. The Authority admin identity directory is also disposable and must not leave the lab root.

Next: Enroll the split roles.

Enroll the split roles

Reader and outcome: The lab administrator enrolls proxy-1 and connector-1 with one-time tokens without putting a token in an argument, configuration file, shell history, or committed output.

Prerequisites: Authority is running with the persistent store and bootstrap listener. The enrollment listener and advertised Proxy addresses match the fixture table. The lab administrator can mint proxy and connector tokens.

Input: Run token creation on authority-1 or a control host with the same persistent store configuration. The token is captured only for immediate secret injection and is never echoed. Run each subsequent role block on the host named in its introduction; do not copy process IDs between hosts.

proxy_token="$("$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" \
  authority create-token --role proxy --ttl 300)"
connector_token="$("$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" \
  authority create-token --role connector --ttl 300)"
test -n "$proxy_token"
test -n "$connector_token"

The approved secret facility transfers each value once and exposes it only to its matching role process as the role-local proxy_token or connector_token; copying the shell variable across hosts by chat, a file, or command history is not an approved transfer.

Before the role-specific block, run this initialization independently on proxy-1 and connector-1. The approved bundle then sets the verified binary and that host's configuration path inside the new root.

umask 077
LAB_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/gdsgate-eval.XXXXXX")"
chmod 700 "$LAB_ROOT"
touch "$LAB_ROOT/.gdsgate-evaluator-lab"
mkdir -p "$LAB_ROOT/evidence"

On proxy-1, use its marked lab root, set GDSGATE_BIN and PROXY_CONFIG from the approved bundle, and start Proxy with the owner-controlled secret injection path. GDSGATE_ENROLL_TOKEN is the source-backed environment input for first enrollment; a service manager should provide it through its secret facility.

GDSGATE_ENROLL_TOKEN="$proxy_token" \
  "$GDSGATE_BIN" --config "$PROXY_CONFIG" proxy \
  > "$LAB_ROOT/evidence/proxy.log" 2>&1 &
PROXY_PID=$!
: "${PROXY_READY_URL:?Set the proxy-1 local /readyz URL from the approved fixture}"
for attempt in $(seq 1 30); do
  curl -fsS "$PROXY_READY_URL" > /dev/null && break
  sleep 1
done
curl -fsS "$PROXY_READY_URL" > "$LAB_ROOT/evidence/proxy-ready.txt"
"$GDSGATE_BIN" --config "$PROXY_CONFIG" doctor --json \
  > "$LAB_ROOT/evidence/proxy-doctor.json"

On connector-1, use its marked lab root and set GDSGATE_BIN from the approved bundle. Connector enrollment reads the token from standard input. It writes the generated role configuration and transport state, but starts no service.

: "${LAB_DOMAIN:?Set LAB_DOMAIN to the approved disposable lab domain}"
: "${CONNECTOR_METRICS_LISTEN:?Set the connector-1 local metrics address}"
printf '%s' "$connector_token" | \
  "$GDSGATE_BIN" join "$LAB_DOMAIN" \
    --role connector \
    --name connector-1 \
    --serve 'lab-postgres=postgres@lab-postgres:5432' \
    --serve 'lab-read=postgres@lab-postgres:5432' \
    --serve 'lab-denied=postgres@lab-postgres:5432' \
    --metrics-listen "$CONNECTOR_METRICS_LISTEN" \
    --output "$LAB_ROOT/connector-generated.toml" \
    --state-dir "$LAB_ROOT/connector-state"
CONNECTOR_CONFIG="$LAB_ROOT/connector-generated.toml"

Start Connector with the generated configuration:

"$GDSGATE_BIN" --config "$CONNECTOR_CONFIG" connector \
  > "$LAB_ROOT/evidence/connector.log" 2>&1 &
CONNECTOR_PID=$!
: "${CONNECTOR_READY_URL:?Set the connector-1 local /readyz URL from the approved fixture}"
"$GDSGATE_BIN" --config "$CONNECTOR_CONFIG" doctor --json \
  > "$LAB_ROOT/evidence/connector-doctor.json"
for attempt in $(seq 1 30); do
  curl -fsS "$CONNECTOR_READY_URL" > /dev/null && break
  sleep 1
done
curl -fsS "$CONNECTOR_READY_URL" > "$LAB_ROOT/evidence/connector-ready.txt"

Expected result: join prints that it joined the cluster, writes the configuration and transport identity, and says that nothing was started. The Proxy and Connector processes remain running. A join token is one-time input; the persisted state directory is used for later renewal.

Positive verification: Inspect the enrolled nodes, node health, and offered resources.

"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority list-nodes
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority list-nodes --health
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority list-offered

The enrolled list must contain proxy-1 and connector-1 with their roles. Health rows must not be absent or failed. The offered list must contain the three declared resource names before the catalog is tested.

Nearest negative: A second use of either one-time token must fail. If a node is absent from health, or an offered resource is not declared, stop before client access. A token passed as a command-line argument is also a failed secret-handling check.

Failure and recovery: For a missing node, inspect the role log and the enrollment endpoint, then mint a new short-lived token. Preserve the existing state directory when the identity is valid.

Security consequence: Proxy and Connector receive different role tokens. Connector dials out to Proxy; the Client never receives the Connector token or the private backend address as a route.

Cleanup: Record node IDs from list-nodes so they can be revoked before state removal. Stop each role before deleting its state directory.

Next: Publish the catalog resources.

Publish the catalog resources

Reader and outcome: The lab administrator declares stable evaluator resource names and confirms that catalog metadata matches Connector offers.

Prerequisites: The operator configuration has an administrative identity and the policy grants the operator catalog management. Use the same Authority store as the running service.

Input: These commands use source-backed set-resource fields. The fixture=evaluator label is lab metadata and must not be copied to production.

"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority set-resource \
  lab-postgres --kind postgres --project evaluator --environment dev \
  --label fixture=evaluator
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority set-resource \
  lab-read --kind postgres --project evaluator --environment dev \
  --parent lab-postgres --label access=read --label fixture=evaluator
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority set-resource \
  lab-denied --kind postgres --project evaluator --environment dev \
  --parent lab-postgres --label access=denied --label fixture=evaluator

The least-privilege transition starts from the approved deny-all baseline. The policy owner adds view for lab-read and lab-denied, then adds connect for lab-read as a Database; lab-denied remains visible but has no matching connect permission. Record the before and after policy identity, validate the exact file, and deploy it through the approved policy workflow. The policy owner must also validate that the provider subject maps to the principal intended for alice@example.test. This page cannot supply a copyable Cedar principal until that identity decision exists, and it does not substitute a permissive testbed policy.

Expected result: Each command prints created followed by the catalog row, or updated when the row already exists. Output details are version-sensitive.

Positive verification: List the catalog and offered resources separately. The catalog proves declaration; the offered list proves that Connector claims to serve the resource. A client listing later proves neither by itself.

"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority list-resources --json \
  > "$LAB_ROOT/evidence/catalog.json"
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority list-offered \
  > "$LAB_ROOT/evidence/offered.txt"

Nearest negative: If a catalog row is missing, or declared=no appears for an offered resource, stop. If Connector claims a resource that the catalog does not contain, do not ask the Client to test it.

Failure and recovery: Correct the resource ID, kind, or Connector backend declaration in the lab owner scope. Restart Connector only after the catalog matches, then repeat node and offer checks.

Security consequence: Catalog declaration is not authorization. A resource can exist in the catalog and remain invisible or denied by policy.

Cleanup: Remove the three disposable catalog rows after node access and audit evidence are collected, or destroy the isolated Authority store with the lab root.

Next: Connect with a pinned transport fingerprint.

Connect

Reader and outcome: The Client writes a configuration only after verifying the transport trust anchor offered by Proxy.

Prerequisites: Proxy is listening on the fixture public address proxy-1:50061. The lab administrator publishes the expected SHA-256 transport CA fingerprint out of band. Set TRANSPORT_CA_FINGERPRINT from that record.

Input: Run on the Client host. connect establishes trust and writes no identity.

: "${TRANSPORT_CA_FINGERPRINT:?Set the published transport CA fingerprint}"
CLIENT_CONFIG="$LAB_ROOT/client.toml"
"$GDSGATE_BIN" connect proxy-1:50061 \
  --fingerprint "$TRANSPORT_CA_FINGERPRINT" \
  --output "$CLIENT_CONFIG"

Expected result: The command writes the client configuration and trust anchor. It may print a gateway version and the notice that the configuration carries no identity and that gdsgate login is next.

Positive verification: Check that the configuration and trust anchor exist, then run the configuration doctor check.

test -s "$CLIENT_CONFIG"
"$GDSGATE_BIN" --config "$CLIENT_CONFIG" doctor --json \
  > "$LAB_ROOT/evidence/client-doctor.json"

Nearest negative: Repeat against a deliberately incorrect fingerprint in a disposable path. The command must refuse the trust mismatch. Never approve a fingerprint prompt when the out-of-band value differs.

Failure and recovery: Confirm the Proxy address, SNI and published fingerprint with the lab administrator. Do not use insecure mode for a networked lab; that mode disables transport verification.

Security consequence: connect proves transport trust only. It does not authenticate alice@example.test, authorize a resource, or prove that a direct backend route is unavailable.

Cleanup: Keep the client config only for this run. Remove it with the lab root after revoking temporary access.

Next: Sign in with the approved identity fixture.

Sign in

Reader and outcome: The Client obtains an identity recognized by Authority.

Prerequisites: Use only the disposable OIDC fixture in the version-matched lab bundle. It defines the issuer, public client, groups, and reserved alice@example.test identity consumed by the evaluator policy. Its fixture credentials are lab-only and must never be reused in a deployment.

Disposable identity only

The committed realm is an evaluation fixture, not a production identity design and not an MFA claim. Do not replace it with a real account. Do not copy its users, credentials, HTTP-only discovery setting, or direct-grant runner into a deployment.

Input: Start the supplied identity fixture, then run one of the supported interactive login flows:

"$GDSGATE_BIN" --config "$CLIENT_CONFIG" login
# Or use the browser flow supported by the disposable provider:
"$GDSGATE_BIN" --config "$CLIENT_CONFIG" login --browser

Expected result: The command reports logged in as followed by the identity resolved from the provider. The expected fixture display identity is alice@example.test; the provider subject is fixed by the versioned realm.

Positive verification: Retain the redacted login result and use the same client configuration for ls and the database proof. Do not copy tokens into the evidence bundle.

Nearest negative: A provider with a wrong issuer, audience, or group claim must fail before resource access. A successful login alone does not grant connect on a database.

Failure and recovery: Return provider error and resolved issuer or audience metadata to the identity owner. Do not weaken policy or switch to a development identity to bypass it.

Security consequence: Authentication establishes who the Client is. Policy authorization and backend reachability remain separate checks.

Cleanup: Sign out or expire the fixture session according to the provider owner's procedure, then remove the local identity cache with the lab root.

Next: See what the identity can reach.

See what you can reach

Reader and outcome: The Client lists only resources visible under the active policy.

Prerequisites: The approved identity is logged in and the catalog contains lab-read and lab-denied.

Input:

"$GDSGATE_BIN" --config "$CLIENT_CONFIG" ls \
  > "$LAB_ROOT/evidence/client-ls.txt"

Expected result: The table header is RESOURCE NAME KIND PORT ENV LABELS. The lab-read and lab-denied rows are both visible because the transition grants view to both. Only lab-read has connect as a Database. Exact spacing and optional columns are version-sensitive.

Positive verification: Confirm the visible resource ID is lab-read, then use the database proof below. Treat the list as a visibility result, not as a database authorization result.

Nearest negative: Visibility of lab-denied is not permission to connect. The explicit db proxy lab-denied refusal and matching audit row are required later.

Failure and recovery: If no resources visible appears, inspect the identity mapping and view policy before testing the data path. If an unexpected resource appears, stop and review policy scope.

Security consequence: Listing does not grant database connect, and a visible resource is not proof that a private backend route is unavailable.

Cleanup: Preserve the redacted table with the evidence bundle and remove it with the lab root after review.

Next: Prove the allowed PostgreSQL query.

A database

Reader and outcome: The Client reaches the seeded PostgreSQL database only through the local gdsgate relay and receives the deterministic result 3.

Prerequisites: The lab-read row is visible, Connector reports it as served, alice@example.test has connect on that Database, and the lab owner supplies the disposable database user, database name, and password through a secret injection path. psql must be installed on the Client.

Input: Start the local database proxy, then query the seeded widgets table. The password is not written in this page or command history.

: "${LAB_DB_USER:?Set the disposable database user through the lab owner}"
: "${LAB_DB_NAME:?Set the disposable database name through the lab owner}"
: "${LAB_DB_PASSWORD:?Inject the disposable database password}"
"$GDSGATE_BIN" --config "$CLIENT_CONFIG" \
  db proxy lab-read --listen 127.0.0.1:5433 \
  > "$LAB_ROOT/evidence/db-proxy.log" 2>&1 &
DB_PROXY_PID=$!
PGPASSWORD="$LAB_DB_PASSWORD" PGCONNECT_TIMEOUT=5 \
  psql -h 127.0.0.1 -p 5433 -U "$LAB_DB_USER" -d "$LAB_DB_NAME" \
  -tAc 'SELECT count(*) FROM widgets;' \
  | tr -d '[:space:]' | tee "$LAB_ROOT/evidence/widgets-count.txt"
test "$(tr -d '[:space:]' < "$LAB_ROOT/evidence/widgets-count.txt")" = 3

Expected result: The proxy log contains db proxy listening and identifies lab-read. It also reports the local address for psql. The query output is 3, because the fixture seeds alpha, beta, and gamma.

Positive verification: Save the query, result, proxy log, and matching authorization evidence. A successful SQL result is the backend protocol witness for the allowed path.

Nearest negative: Stop the proxy and run the direct-backend check in Prove the direct-backend negative. A successful local relay query alone does not prove the direct path is blocked.

Failure and recovery: access denied means policy did not grant database connect; no connector currently serves that resource means the offer or catalog is wrong; a PostgreSQL error after the proxy opens is a backend or credential issue. Diagnose the named layer and do not broaden policy to make a query pass.

Security consequence: db proxy binds a local listener and relays through Proxy. It does not cage another process with an independent network route or database credential.

Cleanup: Stop the local proxy after the query, retain the redacted result, and do not retain LAB_DB_PASSWORD in evidence.

Next: Prove the direct-backend negative.

Prove the direct-backend negative

Reader and outcome: The Client cannot reach lab-postgres:5432 except through the Connector's protected-side network.

Prerequisites: The lab administrator has verified network isolation and has supplied the backend address only to the negative test. The Client must not have a route to the Connector network. A failed database password check is not enough to prove isolation.

Input: Run from the Client host with a short connection timeout. No password is needed to observe a route failure.

: "${BACKEND_HOST:?Set the private backend host for the negative test}"
if PGCONNECT_TIMEOUT=3 psql -w -h "$BACKEND_HOST" -p 5432 \
  -U "$LAB_DB_USER" -d "$LAB_DB_NAME" -c 'SELECT 1;' \
  > "$LAB_ROOT/evidence/direct-backend.out" 2>&1; then
  echo "STOP: the Client reached the private backend directly" >&2
  exit 1
fi

Expected result: The direct connection fails at the Client network boundary. The exact operating-system error is environment-specific and must be retained with the evidence.

Positive verification: Record the source host, destination address, route or firewall evidence, exit status, and stderr. Confirm that the failure is a network refusal or unreachable route, not an authentication rejection returned by PostgreSQL.

Nearest negative: If the Client reaches the backend, stop the evaluation. Do not call the gateway query a successful isolation proof. Remove the direct route or rebuild the isolated lab before continuing.

Failure and recovery: The lab administrator owns routing and firewall correction. Retest from a fresh Client process after the route is removed.

Security consequence: This check measures the network boundary separately from Authority policy. A gateway cannot prevent an already-privileged host process from using an independent route.

Cleanup: Remove the temporary backend address from Client configuration and evidence if it is not needed for review. Keep the failure record until the run is accepted.

Next: Prove policy denial and audit evidence.

SSH

The evaluator slice does not need SSH to establish the database and policy proof. The old route remains available for compatibility. Use the User guide SSH task only after the split lab has passed and the administrator has supplied an SSH resource and policy.

Security consequence: An SSH success would prove a different resource kind; it does not replace the PostgreSQL or direct-backend evidence required here.

Policy denial and audit evidence

Reader and outcome: The evaluator receives a gateway authorization refusal for lab-denied, then independently finds the matching authorization row and verifies the audit chain.

Prerequisites: The policy fixture allows alice@example.test to view and connect to lab-read, refuses database connect for lab-denied, and writes audit anchors to a persistent store. --no-remediate prevents an interactive step-up attempt.

Input: Run the denied request from the Client.

"$GDSGATE_BIN" --no-remediate --config "$CLIENT_CONFIG" \
  db proxy lab-denied --listen 127.0.0.1:5434 \
  > "$LAB_ROOT/evidence/denied.out" 2>&1
DENIED_STATUS=$?
test "$DENIED_STATUS" -ne 0

Expected result: The command exits nonzero and the output identifies lab-denied and access denied. No local database listener remains and the backend receives no successful query.

Positive verification: Find the matching authorization decision through the store route. Move from the Client to authority-1, or to an approved verification workstation with the same persistent-store configuration, before running the next two commands. Set GDSGATE_BIN, AUTHORITY_CONFIG, and this host's marked LAB_ROOT from its approved bundle. This is evidence of the recorded decision, not an integrity proof.

"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" \
  authority --route store audit list \
  --class authorization --target 'Database::"lab-denied"' --json \
  > "$LAB_ROOT/evidence/lab-denied-audit.json"

The JSON must include a row for the denied target and an authorization outcome that corresponds to the Client refusal. If the row is absent, retain the output and stop. Do not treat a generic audit row or an empty listing as a matching decision.

Verify the chain separately:

"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority verify-audit \
  > "$LAB_ROOT/evidence/audit-verify.txt"

With an anchor key configured, the expected summary is audit chain ok (chain + anchors): <rows> rows, <anchors> anchors. If the command warns that only chain-internal links were checked, the audit-integrity acceptance item is not complete.

Nearest negative: A refusal without a matching authorization row, a row without the expected denied outcome, or a chain verification failure is a stop condition. Conversely, a matching audit row without a client-visible refusal does not prove enforcement.

Failure and recovery: Check the target resource, policy version, identity principal, audit store route, and anchor key path. Preserve the failing outputs; repair the lab owner configuration and repeat the complete denial sequence.

Security consequence: Authorization evidence and audit-integrity evidence answer different questions. audit list reads rows. verify-audit checks chain links and, when configured, external anchors. Neither proves host isolation.

Cleanup: Keep the denial output, matching row, and chain summary until the review is complete. Revoke temporary identities and access before deleting state.

Next: Clean up the disposable lab.

Everything else

The canonical evaluator proof is complete after PostgreSQL, direct-backend, policy, and audit checks. Other resource kinds are outside this slice. The existing User guide owns SSH, Kubernetes, TCP, MCP, model, and web tasks; do not infer their support from this database run.

Clean up

Reader and outcome: The lab administrator ends processes, expires access, preserves required evidence, and removes only disposable state.

Prerequisites: The evidence bundle is copied to an approved review location, and node IDs from authority list-nodes are available. Do not delete the only copy of audit output before the review.

Input: Stop the data-plane processes on their owning hosts, then revoke node identities and remove catalog rows through authority-1 while Authority is still ready. Replace variables with IDs from the current run; never guess an ID. The bundle must copy the approved evidence out of each host-local lab root before removal.

On the Client, connector-1, and proxy-1 respectively, stop only the process whose PID that host recorded. After its evidence has been copied, validate the host-local marker and remove that host's exact lab root:

: "${ROLE_PID:?Set ROLE_PID to the process ID recorded on this host}"
kill "$ROLE_PID"
wait "$ROLE_PID" 2>/dev/null || true
case "$LAB_ROOT" in
  "${TMPDIR:-/tmp}"/gdsgate-eval.*) ;;
  *) echo "STOP: unexpected LAB_ROOT $LAB_ROOT" >&2; exit 1 ;;
esac
test -f "$LAB_ROOT/.gdsgate-evaluator-lab"
rm -rf -- "$LAB_ROOT"

On authority-1, revoke the two role identities, remove the catalog rows, and save verification output before stopping Authority:

: "${PROXY_NODE_ID:?Set PROXY_NODE_ID from authority list-nodes}"
: "${CONNECTOR_NODE_ID:?Set CONNECTOR_NODE_ID from authority list-nodes}"
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" \
  authority revoke-node "$PROXY_NODE_ID"
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" \
  authority revoke-node "$CONNECTOR_NODE_ID"
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority remove-resource lab-denied
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority remove-resource lab-read
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority remove-resource lab-postgres
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority list-nodes \
  > "$LAB_ROOT/evidence/nodes-after-revoke.txt"
"$GDSGATE_BIN" --config "$AUTHORITY_CONFIG" authority list-resources --json \
  > "$LAB_ROOT/evidence/catalog-after-cleanup.json"

if test -n "${AUTHORITY_PID:-}"; then
  kill "$AUTHORITY_PID"
  wait "$AUTHORITY_PID" 2>/dev/null || true
fi
case "$LAB_ROOT" in
  "${TMPDIR:-/tmp}"/gdsgate-eval.*) ;;
  *) echo "STOP: unexpected LAB_ROOT $LAB_ROOT" >&2; exit 1 ;;
esac
test -f "$LAB_ROOT/.gdsgate-evaluator-lab"
rm -rf -- "$LAB_ROOT"

Expected result: Services have stopped, node identities are revoked, and the disposable resource rows and local state are gone. A revoked node's existing certificate remains chain-valid until expiry, so short TTLs and network shutdown remain part of cleanup.

Positive verification: Before stopping Authority, inspect the saved node registry and empty catalog output. After stopping the final process, confirm all recorded PIDs are gone and the exact disposable root no longer exists.

Nearest negative: A process that remains alive, a node that is still active, or a secret file outside the disposable root is a cleanup failure. Stop and revoke it before declaring the run complete.

Failure and recovery: If a node ID is unknown, stop and re-read the current enrolled-node output. Do not use delete-node to erase a live identity; deletion is for long-revoked registry rows and is not needed for this lab.

Security consequence: Revocation and expiry reduce future renewal and authorization risk. They do not retroactively erase audit evidence or prove that a previously privileged host had no other credentials.

Cleanup: The final removal target is the exact LAB_ROOT created by mktemp. Do not widen rm -rf to a home directory, workspace, or service state root.

Next: Production handoff.

All-in-one shortcut

For a syntax smoke only, an administrator may use the bundle's all-in-one configuration or run the single-process all mode directly:

: "${ALL_CONFIG:?Set ALL_CONFIG to the approved all-in-one config}"
"$GDSGATE_BIN" --config "$ALL_CONFIG" all

This is a development shortcut. It does not prove split enrollment, a persistent multi-process topology, outbound Connector reachability, or the production TLS boundary. The public listener in this mode is not a substitute for the split lab. Do not use this shortcut as the work item's acceptance result.

Failure and recovery

Use the first failure that names the boundary. Do not skip to a later step after a stop condition.

Failure evidence First diagnosis Safe recovery
Missing artifact, signature failure, or checksum mismatch Release identity is unresolved Stop and return the exact file and status to the release owner
doctor --json exits 2 Configuration is invalid or incomplete Correct the named fixture field, then rerun the affected doctor check
Fingerprint mismatch Client does not trust the offered transport CA Reject the exchange and confirm the out-of-band fingerprint
Join token rejected or already spent Token lifetime or one-time admission failed Mint a fresh role token and preserve valid node state
No node health row or a failed health row Role did not enroll or report readiness Inspect the role log and endpoint reachability before retrying
Offered resource is not catalogued Connector declaration and Authority catalog differ Align IDs and kinds, then restart the Connector if its declaration changed
no resources visible Identity or view policy does not expose the catalog row Check the resolved principal and policy scope
access denied for lab-read Database connect was not granted Inspect policy and identity mapping; do not make policy permissive
no connector currently serves that resource Routing or Connector offer is missing Check list-offered, Connector state, and catalog row
PostgreSQL error after proxy opens Backend address, credentials, or schema issue Diagnose the backend owner; preserve the gateway proof separately
Direct backend query succeeds Client network isolation failed Stop, remove the route, and repeat the negative test
Audit row missing or verify-audit fails Decision recording or chain integrity is unresolved Preserve outputs and repair the Authority store or anchor configuration
OIDC issuer, audience, or group failure First-access identity contract is wrong Return provider metadata to the identity owner; do not use dev mode

Production handoff

Reader and outcome: The evaluator hands a passing lab evidence bundle to the deployment owner without treating the lab as production approval.

The lab does not establish a public artifact publication policy, production identity-provider ownership, high availability, backup and restore, certificate rotation, production network controls, or incident response. The deployment owner must resolve these items before publication:

Handoff decision Evidence or owner action
Artifact and platform support Preserve the administrator or on-premise acquisition path, signature trust root, checksum procedure, and Linux static-musl target; treat any new operating system or architecture as a separate support decision
First identity and MFA Name the issuer, client, audience, redirect or device flow, group mapping, and owner of evaluator and production identities
Persistent state Choose the Authority store, migration owner, backup and restore procedure, and audit anchor-key custody
Split topology Name production Authority, Proxy, Connector, backend, DNS, TLS, and firewall ownership; keep the protected side outbound-only where intended
Least privilege Replace fixture policy with reviewed policy for catalog visibility, connection, query actions, administration, and session controls
Secrets and keys Use the approved secret injection path for enrollment, database credentials, transport keys, identity settings, and audit anchors
Audit and operations Define export, retention, chain verification, monitoring, alerting, revocation, upgrade, rollback, and incident evidence procedures
Acceptance scope Record which positive and negative properties the lab measured and which remain untested in production

Next: Continue with Admin guide, Configuration, Policy, and Operations under their respective owners.

Next

The evaluator path ends after the evidence bundle and cleanup record are attached to the review. For an existing deployment, the User guide covers native client workflows. For deployment and release decisions, continue to the Admin guide and Operations.