Skip to main content
Bridge is a local-first CLI, so its security model is intentionally narrow. Bridge does not try to be a hosted policy engine or secret manager. Instead, it focuses on a few concrete guardrails:
  • keep access scoped to the providers you configure in bridge.yaml
  • fail closed when configuration is missing or incomplete
  • prevent obvious unsafe reads such as filesystem path traversal
  • constrain database reads to validated identifiers and parameterized values
  • return machine-readable errors so humans and agents can react safely

What Bridge secures today

Bridge sits between an agent and your configured backends. Its job is to normalize access, not to replace the permissions of those backends. That means:
  • filesystem access is limited by the directory root you configure
  • database access is limited by the credentials in the connection URI you provide
  • Bridge does not currently add a separate authentication layer on top of those providers
  • Bridge does not currently provide row-level authorization, tenant isolation, or policy rules of its own
If a provider URI points at sensitive data and the underlying credentials allow access, Bridge can expose that data through the normal ls, read, and status flow. Keep provider scope tight.

Configuration safety

bridge.yaml is local, explicit, and required for most Bridge commands. Current safety properties:
  • Bridge only reads bridge.yaml from the current directory
  • it does not search parent directories for another config file
  • provider names must be chosen explicitly
  • provider type inference rejects unsupported URI schemes
  • environment variable expansion fails fast when a referenced variable is missing
Example config:
version: "1"
name: my-project
providers:
  files:
    type: filesystem
    uri: file://./docs
  db:
    type: postgres
    uri: ${DATABASE_URL}

Environment variables fail closed

Bridge expands ${VAR_NAME} references from bridge.yaml before connecting a provider. If a referenced variable is not set, Bridge does not continue with a partial value. It returns env_var_not_set instead. This is important for secrets because it avoids accidental fallbacks like:
  • connecting to the wrong database
  • treating a malformed URI as valid
  • silently removing credentials from a connection string
You can create that config either by editing bridge.yaml directly or by running bridge connect DATABASE_URL --type postgres --as db.

Unsupported URIs fail early

bridge connect only recognizes the schemes Bridge knows how to handle today:
  • file://
  • postgres://
  • postgresql://
Unsupported schemes fail with invalid_uri instead of being guessed or coerced into another provider type. If you use an environment variable name as the bridge connect target, Bridge requires --type because there is no URI scheme to infer from.

Filesystem guardrails

The filesystem provider is designed to keep reads inside the configured root directory. When Bridge reads a filesystem path, it:
  1. canonicalizes the configured root
  2. joins the requested path to that root
  3. canonicalizes the requested target
  4. checks that the resolved path still lives under the root
If the resolved path escapes the root, Bridge returns path_traversal. That means inputs like these are blocked:
  • ../../etc/passwd
  • ../secrets.txt
  • any other path that resolves outside the configured provider root
Other filesystem safety behaviors:
  • Bridge refuses to connect a filesystem provider if the configured path does not exist
  • Bridge refuses to connect if the path is a file instead of a directory
  • reads fail if the target path does not exist under the configured root

Postgres guardrails

The Postgres provider keeps its query surface intentionally small. Today, it only supports:
  • listing tables from the public schema
  • reading a whole table
  • reading a single row by primary key path

Identifier validation

Table names are validated before Bridge uses them in SQL. Bridge only accepts identifiers that match:
[a-zA-Z_][a-zA-Z0-9_]*
That blocks obvious malicious inputs such as:
  • users; DROP TABLE users
  • users where true
  • quoted or multi-statement payloads
Validated identifiers are also quoted before they are inserted into SQL.

Parameterized row values

When you read a specific row such as bridge read users/42 --from db, the row identifier is passed as a bound parameter rather than interpolated directly into the query string. That reduces injection risk for row values even when the primary key type changes between tables.

Scope restrictions

The current Postgres provider is conservative by design:
  • it only lists tables in the public schema
  • it requires a primary key for row reads
  • it refuses suspicious table identifiers before they reach SQL
These limits are partly about product scope, but they also reduce the surface area Bridge has to secure.

Secret handling and redaction

Bridge does not encrypt your provider URIs or manage secrets for you. The recommended pattern is to keep secrets in environment variables and reference them from bridge.yaml. Bridge reads those values from the process environment when commands run. It does not automatically load a .env file. When Bridge needs to show a database URI in user-facing metadata or health output, it redacts the password portion. Example:
postgres://user:***@localhost:5432/mydb
Redaction currently applies to display surfaces such as:
  • Postgres read metadata
  • bridge status output
It does not change the actual connection URI used internally.

Timeout model

Provider operations run inside a global timeout controlled by --timeout.
bridge --timeout 10 status
bridge --timeout 30 read README.md --from files
If a provider operation exceeds that limit, Bridge returns a timeout error instead of waiting indefinitely. This applies to operations such as:
  • connecting to a provider
  • listing entries
  • reading data
  • provider health checks
Current exit-code behavior:
  • exit code 1 for most validation and provider failures
  • exit code 2 for I/O, database, and timeout failures

Error model

Bridge uses one error envelope across commands.
{
  "error": {
    "code": "provider_not_found",
    "message": "Provider 'missing' not found. Available providers: files, db"
  }
}
The main guarantees are:
  • successful JSON goes to stdout
  • error JSON goes to stderr
  • failures are machine-readable
  • failures also carry a human-readable message

Common error codes

CodeWhat it meansTypical fix
config_not_foundbridge.yaml was not found in the current directoryRun bridge init or change into the correct project directory
config_parse_errorbridge.yaml exists but could not be parsedFix the YAML structure or invalid field values
invalid_uriThe provider URI is malformed or uses an unsupported schemeUse a supported file://, postgres://, or postgresql:// URI
invalid_connect_targetA bridge connect target was neither a full URI nor a bare environment variable namePass a literal URI such as postgres://localhost:5432/mydb or an env var name such as DATABASE_URL
missing_provider_typebridge connect received an environment variable name without --typePass --type <provider>, for example --type postgres
invalid_provider_typebridge connect --type named an unsupported providerUse one of the supported provider types
provider_type_conflictbridge connect --type conflicts with the type implied by a literal URIRemove --type or pass the matching provider type
invalid_env_var_namebridge connect received an invalid environment variable namePass a bare name like DATABASE_URL without ${}
env_var_not_setA ${VAR} reference in bridge.yaml could not be resolvedExport the missing environment variable before running Bridge
provider_not_foundThe provider named by --from or remove does not existCheck the provider name in bridge.yaml or run bridge status
provider_errorThe provider rejected the operation or the backend was unavailableInspect the message for the backend-specific failure and verify the provider configuration
path_traversalA filesystem read escaped the configured rootRead a path inside the configured provider directory
invalid_identifierA Postgres table identifier failed validationUse a simple table name that matches the supported identifier pattern
timeoutA provider operation exceeded the configured timeoutIncrease --timeout or fix the underlying connectivity problem
database_errorThe database driver returned an unexpected failureCheck the database connection, permissions, and backend logs

Degraded behavior and recovery

Bridge tries to fail clearly and locally. Examples:
  • if one provider in bridge status is misconfigured, Bridge still reports the others
  • if a single filesystem read fails, Bridge does not mutate your config as part of error handling
  • if environment variable expansion fails, Bridge stops before attempting the provider connection
This keeps failures easier to reason about in scripts and agent flows.

Operational advice

The safest way to run Bridge today is:
  • keep provider roots narrow on the filesystem side
  • use dedicated database credentials with the least access required
  • keep secrets in environment variables, not inline in bridge.yaml
  • use bridge status after adding or changing a provider
  • set --timeout intentionally in automation so long waits fail predictably

Commands

See how Bridge commands expose timeouts, errors, and machine-readable output.

Configuration

Learn how Bridge loads bridge.yaml, resolves provider names, and expands environment variables.

Providers

See the backend-specific behavior and limits that sit behind these guardrails.