Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
25b21b3
Add phase-1 proxy guardrails and strict egress enforcement
theoephraim Jun 6, 2026
8ee198f
Fix proxy guard typecheck and add bumpy changeset
theoephraim Jun 6, 2026
d3f77ba
feat(varlock): add proxy sessions and placeholder-safe proxy behavior
theoephraim Jun 8, 2026
d587520
feat(varlock): harden local proxy for agent runs
theoephraim Jun 10, 2026
373a7da
feat(varlock): proxy security invariants (verified-identity injection…
theoephraim Jun 12, 2026
7505054
feat(varlock): proxy per-call policy (path/method matching + block-de…
theoephraim Jun 12, 2026
b7722f6
feat(varlock): proxy append-only audit log (Invariant #7)
theoephraim Jun 13, 2026
e1e3d1f
refactor(varlock): remove dead proxy config (pin/sign/transform)
theoephraim Jun 13, 2026
3f13c84
feat(varlock): proxy require-approval verdict (Invariant #8)
theoephraim Jun 13, 2026
2f2d106
fix(varlock): allow @proxy in the schema header (detached rules)
theoephraim Jun 13, 2026
1b0983b
feat(varlock): proxy omits unhandled sensitive items by default
theoephraim Jun 13, 2026
fecdc6f
feat(varlock): warn (not just notice) when proxy omits unhandled sens…
theoephraim Jun 13, 2026
1b527ac
feat(varlock): exclude _VARLOCK_* reserved keys from proxy omit policy
theoephraim Jun 13, 2026
d33b059
feat(varlock): dual-form @proxy decorator (function or value), replac…
theoephraim Jun 13, 2026
1993d03
feat(varlock): scoped proxy approvals (once/session/duration) with st…
theoephraim Jun 13, 2026
b70c98e
feat(varlock): proxy approval granularity (approvalEach) + max-durati…
theoephraim Jun 13, 2026
f8990d6
feat(varlock): proxy schema fingerprint covers full definition (value…
theoephraim Jun 14, 2026
1209294
feat(varlock): proxy run attaches to a running proxy (--session / aut…
theoephraim Jun 14, 2026
82a4584
feat(varlock): session-as-record reorg — co-locate per-session files,…
theoephraim Jun 14, 2026
c09d563
docs(varlock): document the credential proxy (guide + decorator & CLI…
theoephraim Jun 16, 2026
ec43223
feat(varlock): @proxy uses array literals for domain/method lists and…
theoephraim Jun 16, 2026
1c087a3
fix(varlock): proxy fingerprint treats domain=a and domain=[a] as ide…
theoephraim Jun 16, 2026
f722d09
feat(varlock): proxy refresh hot-reloads a running daemon (live polic…
theoephraim Jun 17, 2026
676b52f
feat(varlock): blocking proxy refresh via file reload channel (replac…
theoephraim Jun 17, 2026
9f5db17
harden proxy secret isolation + fail-closed config, dedup handlers
theoephraim Jun 18, 2026
3e2c9bb
refactor(proxy): group approval options into an approval={...} object
theoephraim Jun 18, 2026
a3de2f3
refactor(proxy): nest approval granularity in the runtime ProxyRule type
theoephraim Jun 19, 2026
64bec5f
feat(proxy): live request/response log in `proxy start`
theoephraim Jun 19, 2026
dab3c96
fix(proxy): keep the live log from corrupting the approval prompt
theoephraim Jun 19, 2026
b43218d
feat(proxy): color the proxy-start request log
theoephraim Jun 20, 2026
e0e7735
feat(proxy): add `proxy rules` summary, document limitations
theoephraim Jun 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .bumpy/proxy-approval-granularity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

Proxy approvals: per-rule granularity and a max-duration cap.

`@proxy(approval=true)` rules accept an options object for finer control: `@proxy(approval={each="request", maxDuration="15m"})`. `each` is how finely to ask — `host`, `endpoint` (method+path), or `request` (method+path+body) — and `maxDuration` is the ceiling on how long a "yes" is remembered (e.g. `"15m"`, or `0` for always-ask). The object form implies approval is required; `enabled=false` turns the rule back into a plain allow. A standing grant is keyed by the matched rule plus its granularity, so one broad rule can yield per-endpoint or per-exact-request approvals without writing many rules. The cap is enforced proxy-side: the approver's chosen lifetime is clamped to `maxDuration`, so no approver can exceed what the schema allows (always-ask is provably one-tap-per-request).
7 changes: 7 additions & 0 deletions .bumpy/proxy-approval-scopes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

Proxy approvals: approve once, for the session, or for a time window.

The `require-approval` terminal prompt (`varlock proxy start`) now offers scopes — `[y] once`, `[s] this session`, `[m] 15 min` — instead of a plain yes/no. A session- or duration-scoped approval is remembered as a standing grant (stored per session, no secret values) so later requests matching the same `@proxy(approve=true)` rule are auto-approved without re-prompting. Grants are decoupled from the approver behind a store, so the future phone / native-app approver reuses the same mechanism.
7 changes: 7 additions & 0 deletions .bumpy/proxy-array-literals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

`@proxy` uses array literals for lists.

`domain` and `method` now accept an array literal for matching multiple values (`domain=[api.a.com, api.b.com]`, `method=[GET, POST]`), and a detached rule attaches extra items with `keys=[ITEM_A, ITEM_B]` instead of positional `$REF`s. A single value still works (`domain="api.x.com"`).
7 changes: 7 additions & 0 deletions .bumpy/proxy-audit-log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

Add an append-only audit log for proxy sessions (Invariant #7).

Every proxied request now records one JSON line — timestamp, host, method, path, a request fingerprint hash, the matched rule, the decision (allow / deny / blocked-egress / blocked-cleartext), and which managed items were injected (by key name). No secret values, query strings, or request bodies are ever written. Logs are stored per session under `~/.config/varlock/proxy/audit/<uuid>.jsonl` and persist after the session ends. View them with `varlock proxy audit [--session <id>] [--format text|json]`.
7 changes: 7 additions & 0 deletions .bumpy/proxy-default-omit-sensitive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

Proxy: show the agent a placeholder for every sensitive item by default.

Inside `varlock proxy run|start`, any `@sensitive` item the agent sees is replaced with a placeholder — the `@proxy(domain=...)`-routed ones (whose real value is injected at the wire) plus every other sensitive item (which simply isn't injected anywhere). The real value never reaches the child. Use `@proxy=passthrough` to inject the real value (escape hatch) or `@proxy=omit` to withhold an item entirely. Varlock's own `_VARLOCK_*` reserved keys are internal infrastructure and are excluded from this policy.
7 changes: 7 additions & 0 deletions .bumpy/proxy-dual-form-decorator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

Proxy: unify `@proxyPassthrough` into a dual-form `@proxy` decorator.

`@proxy` can now be used as a function — `@proxy(domain=...)` to route a value through the proxy (the agent sees a placeholder) — or as a value: `@proxy=passthrough` injects the real value into the proxied child, and `@proxy=omit` explicitly withholds it (no "no policy set" warning). The two forms are mutually exclusive on a single item. This replaces the separate `@proxyPassthrough` decorator.
21 changes: 21 additions & 0 deletions .bumpy/proxy-phase1-guardrails.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
varlock: patch
---

Add proxy guardrails and strict egress enforcement for run --proxy.

Verified-upstream-identity binding (Invariant #1): secrets are injected only onto upstream TLS connections that validate against the public PKI AND whose cert identity matches the rule-targeted host. A DNS-poisoned / rebound host can't present a valid cert for the target, so it causes a failed connection, never a leaked secret. Cleartext guard (Invariant #2): refuse to inject a secret into a non-TLS (http://) connection; fail closed. Upstream-error path now fails closed (tears down the client connection) rather than half-delivering.

Response scrubbing (Invariant #6): real values reflected in responses are replaced back to placeholders — now including streamed (SSE/chunked) text responses, scrubbed chunk-by-chunk without buffering (so a secret echoed in a stream never reaches the child while streaming still works). Bounded responses gain a post-scrub fail-safe: if a real value somehow survives redaction, the response is withheld rather than leaked. Compressed/binary bodies still pass through unscanned (documented residual).

Per-call policy / static authorization: requests are evaluated as facts (host + method + path) against the `@proxy` rules. A matching `block=true` rule denies the request (fail closed — never reaches upstream), and credential injection is now scoped by path/method too (`getRequestScopedManagedItems`), so a secret can be limited to specific endpoints/methods, not just a host. Modeled as `facts → verdict` (allow|deny|require-approval) with a generic fact bag so domain plugins / non-HTTP protocols slot onto the same seam later. Glob path matching (`*` within a segment, `**` across).

Local-MVP hardening: per-item domain scoping (an item's secret is injected only on hosts its own rule matches), response redaction of headers + uncompressed bodies (compressed bodies pass through — most APIs don't reflect credentials, and forcing identity on every request wasn't worth the cost), schema-fingerprint enforcement on nested commands (closes the @sensitive-downgrade re-load), and correctness fixes (byte-accurate content-length, strict-mode 403 length).

In-memory ephemeral CA: cert generation moved from the `openssl` subprocess to `@peculiar/x509` over WebCrypto (EC P-256). CA and per-host leaf private keys are now generated and held in memory — only the public CA cert is written to disk for child trust. Removes the openssl dependency (portability for the compiled binary) and closes the on-disk private-key exposure. Leaf certs for IP-literal ruled domains now use an IP SAN (clients verify IPs against iPAddress, not dNSName), so IP-based `@proxy` domains work. Validated end-to-end through the compiled binary (real HTTPS MITM) and via an in-process TLS integration test.

Process-ancestry context detection: nested-command guards and placeholder overrides now detect the proxy session by matching a running session's owner/child PID against the current process's parent chain, not just the inherited `__VARLOCK_PROXY_CHILD` marker. Closes the `env -u __VARLOCK_PROXY_CHILD varlock load` bypass that previously recovered real values; a child would now have to daemonize/reparent to escape the process tree. Marker-absent-but-ancestry-positive is logged as a likely bypass probe.

Streaming responses: the proxy no longer buffers `text/event-stream` (and other unknown-length) responses for body redaction — they stream through incrementally, so LLM/agent token-by-token responses work. Body redaction now applies only to bounded, small (<2MB content-length) text responses; header redaction still applies to all responses.

Placeholder generation: the placeholder is functionally load-bearing (a bad one fails an SDK's key-format check at client construction), so generation now favors known-format sources. `@example` derivation was removed (a docs field shouldn't double as a validation-critical placeholder); priority is explicit `@placeholder` → data-type `generatePlaceholder()` → `@type` constraints → generic fallback. Items that land on the generic fallback are flagged and a warning is printed at proxy startup.
7 changes: 7 additions & 0 deletions .bumpy/proxy-refresh-hot-reload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

`varlock proxy refresh` hot-reloads a running proxy.

Editing your schema and running `varlock proxy refresh` re-resolves it in the proxy's trusted context and swaps the live policy — rules, injected secrets, and egress mode — without restarting the proxy or dropping your agent's connection. `refresh` now blocks until the reload completes and then prints how to pick up the new variables (`varlock load` / `varlock run`). Works for both `proxy start` daemons and self-owned `proxy run` sessions.
7 changes: 7 additions & 0 deletions .bumpy/proxy-require-approval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

Add a `require-approval` verdict to the proxy (Invariant #8).

Mark a rule `@proxy(domain="...", approve=true)` and matching requests are held for an out-of-band, request-bound approval before being forwarded. The approval commits to the exact request — method + verified host + path + body hash + nonce + expiry — so a future signed phone-approval relay drops in unchanged. Precedence is block > require-approval > allow (most restrictive wins). The MVP approver is a terminal prompt under `varlock proxy start` (where the proxy owns the terminal; under `varlock proxy run` the child owns stdio, so approval-required requests fail closed). Everything fails closed — denied, timed-out, or unanswerable approvals never reach upstream — and each outcome is recorded in the audit log as `approval-granted` / `approval-denied`.
9 changes: 9 additions & 0 deletions .bumpy/proxy-resolution-hardening.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
varlock: patch
---

Proxy: harden secret isolation and fail-closed config validation.

- A proxied agent can no longer recover a secret by re-resolving the schema (`varlock load`/`printenv`/`run`): inside a proxy session every sensitive item resolves to its placeholder, and `@proxy=omit` items resolve to unset — never the real value. Detection now uses the env marker, the session token, and process ancestry together, so clearing `__VARLOCK_PROXY_CHILD` doesn't bypass the schema-fingerprint guard.
- `@proxy(...)` now rejects unknown options (e.g. a typo like `aproval=true`) and wrong-typed `block`/`approval`/`path` at load time instead of silently producing a permissive rule.
- Type-aware placeholders: `@type=url`/`email`/`uuid`/`md5` get a valid, unique placeholder so SDK format checks pass; all placeholders are unique per item.
7 changes: 7 additions & 0 deletions .bumpy/proxy-root-decorator-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

Fix: allow `@proxy(...)` in the `.env.schema` header for "detached" proxy rules.

`@proxy` is both an item decorator (attached rules) and a root decorator (detached rules), but the header placement check rejected it as a misplaced item decorator, so detached rules — including header-level `block`/`approve` rules — couldn't be authored. A decorator registered as both is now accepted in the header.
10 changes: 10 additions & 0 deletions .bumpy/proxy-rules-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
varlock: patch
---

Add `varlock proxy rules` to summarize the effective `@proxy` configuration.

Prints the routing rules (host / path / method, block / approval) and each
secret's mode — proxied (placeholder, injected), placeholder (sensitive, no
rule), passthrough (real value), or omit — without starting a proxy. Handy for
verifying a schema and seeing what an agent could and couldn't reach.
7 changes: 7 additions & 0 deletions .bumpy/proxy-run-attach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

`varlock proxy run` attaches to a running proxy when possible.

Instead of always starting a fresh (auto-deny) proxy, `proxy run` now attaches to a `proxy start` daemon for the current directory — so the daemon's terminal handles approval prompts while you run the agent in another. It picks the single running session whose directory contains yours (or use `--session <id>`), validates the schema fingerprint (and tells you to restart the proxy on drift, rather than silently routing through a stale one), and injects the session's proxy env + placeholders. Pass `--new` to force a separate fresh proxy. This is the missing piece that makes interactive approval usable from a `run`-style agent command.
7 changes: 7 additions & 0 deletions .bumpy/proxy-schema-fingerprint-full.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

Proxy: the schema fingerprint now covers the full schema definition.

The fingerprint that guards an active proxy session against schema drift (and that `varlock proxy refresh` re-approves) now hashes each config item's value definitions (pre-resolution — no secrets, no I/O) plus every decorator, and all root decorators — instead of only key/sensitivity/required/type. Cosmetic decorators (`@example`, `@docs`, `@docsUrl`, `@icon`, `@deprecated`) are marked `inert` and excluded, and decorator order, named-arg order, comments, and whitespace don't affect it. This closes gaps where a behavioral change left the fingerprint unchanged — e.g. flipping a secret from `@proxy(domain=…)` to `@proxy=passthrough`, changing a `@proxy` domain, or the egress mode.
7 changes: 7 additions & 0 deletions .bumpy/proxy-session-record.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
varlock: patch
---

Proxy sessions are kept as a durable record instead of deleted on stop.

Each session now lives in its own directory (`proxy/sessions/<id>/`) holding its `session.json`, `audit.jsonl`, and `grants.jsonl` together. Stopping a session marks it ended rather than deleting it, so its audit log and approval grants survive for later inspection. `proxy status` shows active sessions by default; pass `--all` to include ended ones.
16 changes: 16 additions & 0 deletions .bumpy/proxy-start-request-log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
varlock: patch
---

`varlock proxy start` now prints a live request log to its terminal.

Each proxied request shows a color-coded one-line decision with the keys injected
on the way out, and each forwarded response shows its status and any keys scrubbed
back to placeholders on the way in (`→` green / `✗` red request, `←` cyan response,
status colored by class, injected/scrubbed key names highlighted):

```
→ GET httpbin.org/get inject: API_TOKEN
← GET httpbin.org/get 200 scrubbed: API_TOKEN
✗ GET example.com/ blocked-egress
```
Loading
Loading