Keep credentials at the boundary.
A practical guide to routing API calls through SecretProxy, from your first binding to deliberate credential rotation.
Your application sends a reference. SecretProxy resolves the credential and inserts it into an outbound request to a configured API.
Sends a reference
Resolves the credential
Receives the request
Start with one request
You need four things: a registered target, a secret version, a binding that connects them, and an allowed source IP. No SecretProxy SDK is required for ordinary HTTP calls.
What changes for your application
# Replace the demo reference with your own generated token.
curl 'https://secretproxy.io/api.stripe.com/v1/balance' \
-H 'Authorization: {{SEC_PRXY_AES_42_demo_key_replace_me}}'The upstream host moves into the proxy URL. A generated reference replaces the credential in the configured header or supported text body. The upstream service continues to receive its expected authentication format.
Understand the trust boundary
The proxy decrypts the selected secret. It is part of your trusted request path. References carry encryption key material and must be protected; they are not public identifiers. A permitted caller can still exercise its allowed API capabilities.
SecretProxy is an OVRLab project. Our engineering publications explain the decisions behind its credential model.
Your first proxied request
Configure the destination and credential, then make a read-only API call from an allowed source.
1. Choose the destination
Sign in to the dashboard and select the organization that owns the integration. In Targets, add the upstream hostname. This example uses api.stripe.com and its read-only /v1/balance endpoint.
2. Create a secret
In Secrets, create a named secret with a credential permitted to read the chosen endpoint. Save the generated reference and encryption key securely when they are returned. Use a test-mode credential while validating the integration.
3. Bind it to the target
Create a binding that selects the target, logical secret and secret version. Set the injection type to header, the header name to Authorization, and its template to Bearer {{value}}.
| Setting | Value |
|---|---|
| Target | api.stripe.com |
| Secret version | The version you just created |
| Injection | header |
| Header | Authorization |
| Template | Bearer {{value}} |
4. Allow the source
Add the service’s public outbound IPv4 or IPv6 address in IP management. Individual addresses are supported; CIDR ranges are not. If the address is scoped to a service, configure that service’s target access as well.
5. Send a request
# Replace the demo reference with your own generated token.
curl 'https://secretproxy.io/api.stripe.com/v1/balance' \
-H 'Authorization: {{SEC_PRXY_AES_42_demo_key_replace_me}}'Replace the entire fictional token with your own generated reference. A successful call returns the upstream response. This documentation never sends the example for you.
6. Check the result
If source access fails, check the actual public egress address. If a reference fails, check its secret, key, version and target binding. If the upstream rejects the request, check credential permissions and the header template. The troubleshooting guide follows that same order.
The request path
How source access, target resolution and credential injection fit together.
From caller to destination
- Identify the source. The hosted proxy resolves the caller’s public IP against allowed addresses, establishing the organization and optional service scope.
- Resolve the target. The first path segment identifies a registered upstream host. The remaining path and query string become the upstream request.
- Read references. Headers and eligible text bodies are scanned for the current
SEC_PRXYgrammar. - Resolve the binding. The target and secret must belong to the resolved organization. A service-scoped source must also satisfy its access policy.
- Verify and decrypt. The supplied key is checked against the selected version’s key verifier before decryption and injection.
- Forward the call. SecretProxy sends the request over HTTPS and returns the upstream response.
What the URL means
https://secretproxy.io/api.stripe.com/v1/balanceapi.stripe.com is the target. /v1/balance is the upstream path. Do not put a credential or reference in the URL: injection happens in headers or eligible bodies.
What remains your responsibility
Use least-privilege upstream credentials and controlled egress. The proxy can see the decrypted credential, and a compromised permitted workload can still make permitted calls. SecretProxy does not authorize your application’s users or decide which action an AI agent should take.
Body and transport limits
Body scanning is limited to supported UTF-8 text payloads. Compressed, binary, unsupported-charset or oversized bodies are not a place to put references. The public proxy currently uses a 10-second upstream timeout; measure your actual workload rather than assuming a latency guarantee.
Credential references
The reference format, its sensitive key material, and the difference between a logical secret and a version.
Read the format
{{SEC_PRXY_AES_42_demo_key_replace_me}}| Part | Meaning |
|---|---|
| SEC_PRXY | The required token prefix |
| AES | The encryption mode. The parser also accepts BYOK and BYOC. |
| 42 | The logical secret ID, not a secret-version row ID |
| demo_key_replace_me | Fictional key material. Use the complete generated token. |
Keys contain 16–64 letters, digits or underscores. A trailing _vN suffix selects a positive version number. Avoid inventing a token from a secret’s display name.
Pin a version
{{SEC_PRXY_AES_42_demo_key_replace_me_v2}}An unversioned reference follows the binding’s selected version. A pinned reference requests that version number for the logical secret, while still requiring an allowed target binding and the matching encryption key.
Protect the caller’s reference
A reference carries key material. Store it in protected runtime configuration, redact it from logs, and keep it out of prompts. Losing an upstream secret and losing a usable reference are different incidents, but both require access review.
Encryption modes
Secret creation generates an AES key when encryption_key is omitted. Supplying a valid key uses BYOK mode. BYOC is accepted by the proxy grammar; this guide does not promise a separate hosted BYOC provisioning workflow.
Targets and bindings
A target names the destination. A binding chooses which credential that destination can receive.
Register a hostname
A target uses a bare hostname such as api.stripe.com. Keep the API path in the request, not the target record. The management API normalizes schemes and trailing slashes, but storing a clean hostname makes the configuration easier to inspect.
Connect the secret
A binding links the target, logical secret and selected version. It also specifies header or body injection and an optional formatting template. These records must belong to the same organization, and the selected version must belong to the selected secret.
Keep scopes deliberate
Use separate secrets and bindings when integrations need different privileges or rotation schedules. A registered host is a destination boundary, not a guarantee that every endpoint on it is appropriate for every caller.
Continue to managing bindings or the bindings API.
Header and body injection
Choose where a reference may appear and how its resolved value is formatted.
Header injection
For bearer authentication, bind the secret to Authorization with Bearer {{value}}. Send only the generated reference as the header value; the template supplies the bearer prefix.
Authorization: {{SEC_PRXY_AES_42_demo_key_replace_me}}For APIs that expect a raw key in X-Api-Key, configure that header and omit the bearer template. Header bindings must appear in their configured header.
Body injection
Use a body binding only when the upstream API expects a credential in a supported text payload. JSON, XML and other supported textual media types must be UTF-8 and uncompressed. The current scan limit is approximately 1 MiB; use header injection for larger or binary payloads.
{"credential": "{{SEC_PRXY_AES_42_demo_key_replace_me}}", "action": "status"}A header-only reference in a body, or a body-only reference in a header, is rejected. Missing content types, unsupported encodings and compressed bodies are not scanned. Do not assume a reference in an unscanned body will be replaced.
Formatting and escaping
Templates perform text substitution. They do not provide arbitrary JSON or XML escaping. Choose credentials and payload formats accordingly, and validate the resulting request with a disposable test integration.
Rotate a credential
Create the next value, test it, then move each binding deliberately.
Create a new version
Updating a secret creates a new immutable version record. Existing bindings remain on their selected version until you promote them. The logical secret ID stays stable; the version row ID and version number are separate values.
Plan for the encryption key
The API generates a new key when encryption_key is omitted, including during rotation. Save the new reference returned by the update. If the selected version has a different encryption key, update caller references as part of the rollout.
Promote the binding
- Create and retain the new secret version’s reference securely.
- Validate the new value and key against a controlled integration.
- Promote the intended binding to the new version row ID.
- Update callers where the key changed, then check upstream behavior.
- Retire the old upstream credential after consumers have moved.
The management endpoint is POST /api/bindings/:id/promote with a secret_version_id. Promotion changes the binding; it does not revoke a credential at the upstream provider.
Rollback and explicit pins
A retained earlier version can be selected again if its upstream credential remains valid. Callers need that version’s matching key. Explicit _vN pins keep requesting their chosen version and do not follow binding promotion. Deleting a logical secret removes its versions and bindings.
Connect an agent tool
Keep upstream credentials out of model context while giving a tool narrowly defined HTTP capabilities.
First complete the quick start: register the upstream host, create a secret and binding, and allow the workload’s outbound IP. All examples use fictional references that must be replaced before use.
Keep the reference in the adapter
The tool’s HTTP adapter should obtain its reference from protected runtime configuration. The model supplies structured task arguments; it should not receive the reference, choose arbitrary upstream hosts, or construct authentication headers.
// Run inside the tool adapter, not in a model prompt.
// SECRET_PROXY_REFERENCE contains your protected generated token.
const response = await fetch(
'https://secretproxy.io/api.stripe.com/v1/balance',
{ headers: { Authorization: process.env.SECRET_PROXY_REFERENCE } }
);
if (!response.ok) throw new Error('Balance request failed');
const balance = await response.json();Authorize the action separately
Fix the destination and allowed operation in the tool implementation. Validate arguments and enforce your agent’s approval policy for consequential actions. A successful SecretProxy access check is not approval of the agent’s intent.
Control outputs and logs
Return only the data needed by the model. Redact authentication material from request tracing, exception handling and tool transcripts. Use an allowed stable outbound IP for the adapter.
This is an ordinary HTTP integration pattern. It does not require or imply a SecretProxy-specific agent SDK or MCP server.
Builds and GitHub Actions
Call an allowed upstream API from a runner with controlled outbound access.
First complete the quick start: register the upstream host, create a secret and binding, and allow the workload’s outbound IP. All examples use fictional references that must be replaced before use.
Choose the runner
Use a self-hosted runner or another runner configuration with a stable outbound IP you control. Register that individual address. Do not copy broad GitHub Actions CIDR ranges into SecretProxy: CIDR entries are not supported.
Store the reference
Save the generated reference in a protected GitHub Actions secret named SECRET_PROXY_REFERENCE. Restrict which workflows and environments can use it, and do not print it in logs.
jobs:
balance-check:
runs-on: self-hosted
steps:
- name: Read test balance
env:
SECRET_PROXY_REFERENCE: ${{ secrets.SECRET_PROXY_REFERENCE }}
run: |
curl --fail --silent --show-error \
'https://secretproxy.io/api.stripe.com/v1/balance' \
-H "Authorization: $SECRET_PROXY_REFERENCE"Limit workflow access
Keep privileged references away from untrusted pull-request code. Use narrowly scoped upstream credentials, and rotate the reference when its encryption key changes. A SecretProxy reference is still sensitive CI configuration.
Services and virtual machines
Use an existing HTTP client from a service with a predictable public egress address.
First complete the quick start: register the upstream host, create a secret and binding, and allow the workload’s outbound IP. All examples use fictional references that must be replaced before use.
Establish outbound access
Register the public IP visible to the hosted proxy. Behind NAT, this is the gateway’s public address, not the machine’s private address. Keep service-scoped IP records and target access policies aligned.
Make the request
import os
import requests
response = requests.get(
'https://secretproxy.io/api.stripe.com/v1/balance',
headers={'Authorization': os.environ['SECRET_PROXY_REFERENCE']},
timeout=15,
)
response.raise_for_status()Operate the integration
Set timeouts and deliberate retry policies. Retrying a failed write may duplicate an upstream action; use the provider’s idempotency mechanism where available. Keep the reference out of exception dumps and HTTP debug logging.
Serverless functions
Plan source access before moving an outbound integration into a serverless runtime.
First complete the quick start: register the upstream host, create a secret and binding, and allow the workload’s outbound IP. All examples use fictional references that must be replaced before use.
Plan the egress address
A runtime with changing shared outbound addresses is difficult to allow narrowly. Configure controlled egress for your function and register its individual public IP. For AWS Lambda, a VPC with an appropriate NAT setup is one way to establish stable public egress.
Use the normal HTTP pattern
Keep the generated reference in protected function configuration. Use the same destination and header pattern shown in the service integration. No background credential-fetch loop is required by SecretProxy.
Budget for the extra hop
Measure cold starts, proxy transit and the upstream API together. The hosted proxy currently has a 10-second upstream timeout; account for that in the function’s own deadline and retry policy.
Kubernetes workloads
Connect pods to the hosted proxy through controlled cluster egress.
First complete the quick start: register the upstream host, create a secret and binding, and allow the workload’s outbound IP. All examples use fictional references that must be replaced before use.
Use a stable cluster exit
Register the cluster’s public egress IP, not a pod address. Review shared-egress implications: multiple workloads behind the same address share a source identity at the hosted proxy.
Configure the caller
Supply the generated reference using your cluster’s protected configuration practices. The upstream credential stays outside the pod for configured proxied calls, but the reference in the pod still needs protection.
Keep deployment models distinct
This guide covers Kubernetes calling the hosted service. A supported in-cluster SecretProxy distribution, Helm chart or sidecar is not provided by these instructions. Discuss private deployment requirements with the team.
Containers
Use the host or gateway’s outbound address and keep references out of images.
First complete the quick start: register the upstream host, create a secret and binding, and allow the workload’s outbound IP. All examples use fictional references that must be replaced before use.
Configure at runtime
Supply the reference through your deployment’s protected runtime configuration. Do not bake it into an image, Dockerfile, build argument or source repository. Container isolation does not make a reference safe to disclose.
Check the network path
The hosted proxy sees the public address after container networking and NAT. Register that address and confirm service-scoped access before testing the call.
Send a regular HTTP call
The containerized service can use the same Python or HTTP client pattern as any other application. This is a client integration, not an instruction to run a SecretProxy container image.
Local development
Validate an integration with disposable credentials and deliberate source access.
Test against a disposable target
Use an upstream test account and a credential with limited permissions. Register your current public egress IP, accounting for VPNs and network changes. Remove temporary access when the test is complete.
# Replace the demo reference with your own generated token.
curl 'https://secretproxy.io/api.stripe.com/v1/balance' \
-H 'Authorization: {{SEC_PRXY_AES_42_demo_key_replace_me}}'Avoid public credential echoes
Services that echo request headers can return the resolved credential in their response. Never send a production credential to a public echo endpoint. Use a controlled mock or disposable test credential instead.
Working on the repository
Repository contributors should follow its README for local API, dashboard and proxy setup. There is no separate published secretproxy CLI install command supplied by this repository.
Manage targets
Name and organize the upstream APIs an organization integrates with.
Add a destination
In the dashboard, choose the correct organization, then create a target with a name, hostname and optional description. Keep environment and ownership clear in the name or description.
Change a destination
Review every binding and caller before changing a hostname. A hostname change affects where future matching requests can resolve credentials. Verify the new destination with a controlled test.
Remove a destination
Deleting a target also removes its associated bindings. Stop or update callers first. Deleting configuration in SecretProxy does not revoke an upstream API credential.
See the targets API for the administrative contract.
Manage secrets
Create encrypted values, preserve generated references and manage versions.
Create a logical secret
A secret name identifies the integration for your team. Its logical ID remains stable across versions. Values must contain at least eight characters. The API encrypts the value and returns the generated reference and key on creation.
Retain the key securely
Listing secrets returns masked information, not the original key. Save the generated reference through an appropriate protected process. If the key is lost, create a new credential version and update affected callers.
Rotate or delete
Updating creates a version; it does not automatically promote bindings. Deleting a logical secret removes its versions and associated bindings. For a controlled rollout, follow credential rotation.
Manage bindings
Control the target, version and injection policy for each integration.
Create the relationship
Select a registered target, a logical secret and one of that secret’s versions. Choose header or body injection. For a header binding, specify the expected header name and optional template.
Review the effective request
Check whether the caller sends a pinned or unversioned reference. A pin can continue selecting an older version even after the binding is promoted. Both require the matching key and a valid target binding.
Promote deliberately
Use the promotion action for the binding you intend to move. The API takes the secret-version row ID, not its human-readable version number. Review the rotation guide before changing production consumers.
Source IP access
Establish which public source addresses may use the hosted proxy.
Register individual addresses
Allowed IP records accept IPv4 or IPv6 addresses. CIDR ranges are not supported. Use the public address seen after NAT, not a private interface or pod address.
Choose the scope
A service-scoped record requires a service in the same organization; configure the service’s access policies for the required targets. A tenant-scoped record does not take a service ID. Use the narrowest scope that fits the workload.
Understand shared egress
Several workloads behind one gateway can present the same source IP. IP access alone does not identify a particular process or user. Keep references and upstream permissions constrained as well.
Change access carefully
Source lookups are cached briefly by the hosted proxy. Validate changes from the intended workload, and account for up to roughly one minute of cached lookup state. Do not treat deleting an IP record as a substitute for revoking a compromised upstream credential.
Management API
Session authentication, organization scope and the response contract for administrative operations.
These are administrative endpoints at https://api.secretproxy.io. They require an authenticated session and operate within its active organization. Use the dashboard for interactive administration. A proxy reference does not authenticate the management API.
Authentication and scope
The management API uses session cookies, not proxy reference tokens. The authenticated session establishes the active organization and user permissions. Reader access does not grant write permission; the target, secret and binding write routes require developer-level access or above.
Read a resource
# cookies.txt must contain a valid session obtained through sign-in.
# Keep this file private and out of source control.
curl --fail-with-body 'https://api.secretproxy.io/api/targets' \
--cookie cookies.txtThe cookie file is illustrative. Do not export a session into a shared runner or assume a browser session is a supported long-lived automation credential.
Responses
{"success": true, "data": []}Most reads and writes return a JSON envelope. Created resources use HTTP 201; successful deletion uses 204. Authentication, validation, authorization and conflict failures return non-success status codes. Callers should check both the status and the documented payload for their operation.
Field naming
Creation and update payloads below use the route’s snake_case field names. Returned data uses the implementation’s camelCase fields. Keep logical secret IDs, version row IDs and version numbers distinct.
Targets API
Create, inspect, update and remove registered upstream destinations.
These are administrative endpoints at https://api.secretproxy.io. They require an authenticated session and operate within its active organization. Use the dashboard for interactive administration. A proxy reference does not authenticate the management API.
Endpoints
| Method | Path | Operation |
|---|---|---|
| GET | /api/targets | List targets |
| GET | /api/targets/:id | Read a target |
| POST | /api/targets | Create a target |
| PUT | /api/targets/:id | Update supplied fields |
| DELETE | /api/targets/:id | Delete target and associated bindings |
Create a target
{
"name": "Stripe test",
"base_url": "api.stripe.com",
"description": "Read-only test integration"
}| Field | Requirement |
|---|---|
| name | Required; 1–255 characters |
| base_url | Required hostname; normalized to lowercase without scheme or trailing slash |
| description | Optional; up to 1,000 characters |
Update and delete
PUT accepts the same fields as optional updates. DELETE removes bindings associated with the target. Review dependent consumers before removing or changing a destination.
Secrets API
Create logical secrets and encrypted versions without confusing their identifiers.
These are administrative endpoints at https://api.secretproxy.io. They require an authenticated session and operate within its active organization. Use the dashboard for interactive administration. A proxy reference does not authenticate the management API.
Endpoints
| Method | Path | Operation |
|---|---|---|
| GET | /api/secrets | List latest versions with masked values |
| GET | /api/secrets/:id | Read a logical secret |
| GET | /api/secrets/:id/versions | List its versions |
| POST | /api/secrets | Create a secret and first version |
| PUT | /api/secrets/:id | Create the next version |
| DELETE | /api/secrets/:id | Delete the secret, versions and bindings |
Create a secret
{
"name": "stripe-test",
"value": "disposable_test_value"
}The value above is fictional. A real value must have at least eight characters. An optional encryption_key uses BYOK mode: 16–64 letters, digits or underscores, without a reserved trailing _v<number>.
Handle the result
Creation returns the logical id, versionId, version number, encryptionKey and placeholder. Capture the generated key and reference securely. Do not log the complete response.
Create the next version
PUT requires value and optionally encryption_key. Omitting the key generates a new one. The response includes secretId, versionId, version, encryptionKey and placeholder. Bindings are not automatically promoted.
Bindings API
Connect targets to logical secrets and select the version used by unpinned callers.
These are administrative endpoints at https://api.secretproxy.io. They require an authenticated session and operate within its active organization. Use the dashboard for interactive administration. A proxy reference does not authenticate the management API.
Endpoints
| Method | Path | Operation |
|---|---|---|
| GET | /api/bindings | List bindings |
| GET | /api/bindings/:id | Read a binding |
| POST | /api/bindings | Create a binding |
| PUT | /api/bindings/:id | Update a binding |
| POST | /api/bindings/:id/promote | Promote its selected secret version |
| DELETE | /api/bindings/:id | Delete a binding |
Create a binding
{
"external_target_id": 1,
"secret_id": 42,
"secret_version_id": 7,
"injection_type": "header",
"header_name": "Authorization",
"header_template": "Bearer {{value}}"
}| Field | Requirement |
|---|---|
| external_target_id | Required target ID |
| secret_id | Required logical secret ID |
| secret_version_id | Required version row ID belonging to that secret |
| injection_type | Required: header or body |
| header_name | Required for header injection |
| header_template | Optional template containing {{value}} |
The IDs are illustrative. All referenced resources must belong to the session’s active organization. Creation does not accept an arbitrary named placeholder; callers use the secret’s generated reference.
Promote a version
{"secret_version_id": 8}Send this payload to POST /api/bindings/:id/promote. Use a version row ID from the same logical secret. A changed encryption key also requires updating callers.
Update or remove
PUT accepts partial updates to binding fields. Changing to header injection requires a header name. Deleting a binding removes that target relationship; it does not revoke the credential at its upstream provider.
Troubleshoot a request
Work from source access to the upstream API, without exposing credentials in diagnostics.
Check the boundary that failed
| Symptom | What to inspect |
|---|---|
| 403 before forwarding | Public egress IP, organization/service scope and target access policy |
| Unknown target | Registered hostname and first URL path segment |
| Malformed reference | Exact generated token, braces, mode, ID, key length and optional version suffix |
| Unresolved reference or version | Target binding, selected secret/version and matching key |
| Injection policy rejection | Configured header name and header-versus-body placement |
| Upstream 401 or 403 | Credential validity, upstream permissions and header template |
| 502 or 504 | Upstream reachability and the proxy’s request timeout |
Collect safe diagnostics
Record the time, operation, upstream host, HTTP status and relevant request identifier if present. Remove reference tokens, cookies, credentials and sensitive request bodies before sharing logs.
After a rotation
Check whether the caller is explicitly pinned. Confirm which version the binding selects and whether the new version uses a new encryption key. Promoting a binding alone cannot repair an old caller reference when the key changed.
Get help
Contact hello@ovrlab.io with a sanitized reproduction. Never send production credentials or complete reference tokens.
Deployment and trust
Review the hosted request boundary and discuss requirements that need a different deployment model.
The hosted service
The public proxy, dashboard and management API are separate surfaces. The proxy resolves credentials for configured requests; the management API administers organizations, targets, secrets and bindings.
Review your security model
Decide which sources can call the proxy, how references are stored, how upstream permissions are limited, and how credential incidents are handled. The proxy decrypts credentials and is therefore a trusted component, not a zero-knowledge service.
Private deployment requirements
Private or in-cluster deployment, organization-specific support and contractual requirements need a discussion with the team. They are not implied by the hosted quick start or presented here as a self-service Helm, Docker or Terraform installation.
Infrastructure automation
The current boundary between documented API operations and tooling that is not shipped.
Provider availability
This repository does not ship a supported SecretProxy Terraform provider. Earlier provider installation and resource examples have been removed because they do not describe an available implementation.
Use the documented contracts
The management API reference documents existing administrative operations. Those endpoints currently use session authentication; do not assume a long-lived service-account token workflow.
Plan automation with the team
If Terraform or unattended provisioning is a requirement, contact the team to discuss authentication, ownership and lifecycle needs before building a production dependency on it.