How the server resolves its .tapes/ directory and settings, and how the client resolves its one server URL.
The server and the client keep separate configuration with different resolution rules. Read the section for the binary you are configuring; they do not share a precedence chain.
The server’s configuration
Section titled “The server’s configuration”Tapes stores configuration and credentials in a .tapes/ directory. Resolution order is:
--config-dir <directory>;.tapes/in the current directory;~/.tapes/;- built-in defaults if none exists.
Create a project-local directory with defaults or a provider preset:
tapes inittapes init --preset anthropic# presets: openai, anthropic, ollamaA project-local .tapes/ takes precedence over the home directory. This is useful for per-project provider and database settings, but is also the first place to check when an expected global setting appears ignored.
Precedence
Section titled “Precedence”For commands that bind a setting, precedence is:
- CLI flag;
TAPES_...environment variable;config.tomlvalue;- built-in default.
Dots become underscores in environment names, for example TAPES_PROXY_LISTEN and TAPES_STORAGE_POSTGRES_DSN.
Manage settings
Section titled “Manage settings”tapes config listtapes config get proxy.providertapes config set proxy.provider anthropictapes config set proxy.upstream https://api.anthropic.comUseful supported keys include:
| Key | Purpose | Default |
|---|---|---|
storage.postgres_dsn |
Capture and derived PostgreSQL database | unset |
proxy.provider |
anthropic, openai, or ollama |
ollama |
proxy.upstream |
Upstream provider base URL | http://localhost:11434 |
proxy.listen |
Proxy listen address | :8080 |
proxy.project |
Session project tag | auto-detected from Git when unset |
api.listen |
Read API listen address | :8081 |
api.web_ui |
Minimal browser UI at / |
false |
ingest.listen |
Private ingest listen address | :8082 |
client.proxy_target |
Proxy URL used by clients | http://localhost:8080 |
client.api_target |
API URL used by clients | http://localhost:8081 |
vector_store.target |
pgvector PostgreSQL DSN | primary PostgreSQL DSN when unset |
embedding.provider |
ollama or openai |
ollama |
embedding.target |
Embedding service URL | http://localhost:11434 |
embedding.model |
Embedding model | embeddinggemma |
embedding.dimensions |
Vector dimensions | 768 |
opencode.provider / opencode.model |
Saved OpenCode choice | unset |
telemetry.disabled |
Disable CLI usage telemetry | false |
update.disabled |
Disable update checks | false |
cassettes = ["https://host/openapi"] is a top-level array for operator-managed cassette OpenAPI URLs; it is not a dotted config set field. See Cassettes for the manifest, deployment responsibilities, and runtime behavior.
The internal listener
Section titled “The internal listener”The API server can run a second listener carrying one endpoint, GET /internal/readiness/evidence. It reports what this process loaded and what
admitting that configuration produced: the instance’s identity, a digest of the
cassette source list in effect, and each source’s admission result with its
manifest and OpenAPI digests. It publishes no configuration value and no
secret.
It answers a question the read API cannot. /ping returns pong
unconditionally, and cassette discovery resolves asynchronously after the
process is already serving, so a healthy probe does not mean the cassettes are
admitted yet. An orchestrator that needs to know a configuration actually took
has to ask each serving instance, and has to be told admission results rather
than intent.
It is a separate listener rather than a path on the API server because a deployment may put a gateway in front of the API that rewrites a public path prefix onto its root — which would make any path added there publicly reachable. Expose this one as a container port and keep it off the Service.
| Variable | Purpose |
|---|---|
TAPES_INTERNAL_LISTEN |
Internal listener address. Unset, no listener runs. Use :8092. |
TAPES_INTERNAL_TOKEN |
Bearer token every request must present. |
Both are read from the environment only — never from config.toml and never
from a flag, because a token in a config file outlives the process that needed
it and one in a flag is readable from the host’s process table. Setting
TAPES_INTERNAL_LISTEN without TAPES_INTERNAL_TOKEN fails startup rather
than serving the endpoint unauthenticated. A request without the token gets a
401 with no body.
The instance block is filled in from the environment too, and each field is simply absent when unset. In Kubernetes these come from the downward API:
| Variable | Field |
|---|---|
TAPES_POD_NAME |
metadata.name |
TAPES_POD_UID |
metadata.uid |
TAPES_POD_IP |
status.podIP |
TAPES_NODE_NAME |
spec.nodeName |
TAPES_REPLICA_SET |
owning ReplicaSet name, when the deployment can supply it |
TAPES_IMAGE_DIGEST |
the running image’s digest |
Example
Section titled “Example”version = 0
[storage]postgres_dsn = "postgres://tapes:tapes@localhost:5432/tapes?sslmode=disable"
[proxy]provider = "anthropic"upstream = "https://api.anthropic.com"listen = ":8080"
[api]listen = ":8081"
[client]proxy_target = "http://localhost:8080"api_target = "http://localhost:8081"
[vector_store]target = "postgres://tapes:tapes@localhost:5432/tapes?sslmode=disable"
[embedding]provider = "ollama"target = "http://localhost:11434"model = "embeddinggemma"dimensions = 768Store provider secrets with tapes auth openai or tapes auth anthropic, or use the provider’s environment variable. Do not put API keys in config.toml.
The client’s configuration
Section titled “The client’s configuration”tapesctl keeps API and ingest URLs in ~/.tapes/config.toml:
api-url = "http://localhost:8081"ingest-url = "http://localhost:8082"Write them with:
tapesctl config set api-url http://localhost:8081tapesctl config set ingest-url http://localhost:8082tapesctl config pathThree differences from the server’s rules are worth stating outright:
- There is no project-local layer. The client always reads
~/.tapes/config.toml, whatever directory you run it from. A.tapes/in the current directory configures the server and is invisible to the client. - The API default is explicit.
--api-url,TAPES_API_URL, andapi-urlconfigure reads; absent all three it useshttp://localhost:8081. - Ingest has its own setting.
--ingest-url,TAPES_INGEST_URL, andingest-urlconfigure capture; absent all three it useshttp://localhost:8082.
config set edits the file in place, so comments, ordering, and keys the client
does not know about survive it. config get lists only keys it knows and that
are set, so empty output does not mean an empty file.
The file sits beside ~/.tapes/logs, ~/.tapes/skills, and ~/.tapes/codex-app
rather than under $XDG_CONFIG_HOME, so one directory holds everything the
client writes.