Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bc78ae8
feat(policy): support cred_inject value_prefix; drop opencode+anthrop…
vessux Jun 12, 2026
3f5bc53
feat(anthropic): host-side OAuth PKCE /login client
vessux Jun 12, 2026
8a9dff2
feat(sandbox): provider sandboxFiles delivery + CLAUDE_CONFIG_DIR
vessux Jun 12, 2026
135d382
feat(anthropic): replace setup-token with OAuth subscription provider
vessux Jun 12, 2026
6ec03ef
feat(ensure-provider): import OAuth profile, seed-once, configure gat…
vessux Jun 12, 2026
a0ab6a1
chore: render value_prefix into default policy + document it
vessux Jun 12, 2026
716a73b
chore: fix stale wire-proof comment + align test fixture token_url
vessux Jun 12, 2026
4ba4e4c
chore(sandbox): bump OPENSHELL_FORK_TAG to v0.6.5 (value_prefix)
vessux Jun 12, 2026
7c140cd
fix(anthropic): use console.anthropic.com OAuth endpoints + canonical…
vessux Jun 13, 2026
fd4e96f
fix(anthropic): match Claude Code subscription OAuth exactly (claude.…
vessux Jun 13, 2026
fa60c81
feat(sandbox): OPENLOCK_OPENSHELL_BIN dev override for the openshell CLI
vessux Jun 13, 2026
4b2defb
feat(anthropic): parseClaudeOauthBlob — map CC credential to LoginResult
vessux Jun 13, 2026
a26deae
feat(anthropic): claudeKeychainService — deterministic CC keychain it…
vessux Jun 13, 2026
fbce85c
feat(anthropic): importFromClaudeCode orchestration with injectable deps
vessux Jun 13, 2026
7f5bfbc
feat(anthropic): import token from Claude Code login; drop OAuth hand…
vessux Jun 13, 2026
e238146
feat(sandbox): --no-attach flag plumbing
vessux Jun 13, 2026
1908d0d
feat(sandbox): --no-attach detached create (skip harness attach), clo…
vessux Jun 13, 2026
efb071b
style(anthropic): biome format import module + test
vessux Jun 13, 2026
c914755
fix(anthropic,sandbox): macOS keychain→file harvest fallback; reset d…
vessux Jun 13, 2026
8c7522e
docs: README — first sandbox run runs 'openlock login' (subscription …
vessux Jun 13, 2026
9e825b0
fix(ensure-provider): profile import is not idempotent — probe via 'p…
vessux Jun 13, 2026
ee668a2
fix(sandbox): tether must not inherit stdout — detached create (--no-…
vessux Jun 13, 2026
69600a1
test(ensure-provider): extract fake-gateway responder to bound cognit…
vessux Jun 13, 2026
4f345f1
refactor(sandbox): TETHER_STDIO const + tripwire test — standardise g…
vessux Jun 13, 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: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ openlock validate /path/to/your/repo # lint the manifest + policy
openlock sandbox /path/to/your/repo # launch (path defaults to cwd)
```

`sandbox` requires an `.openlock/` (run `openlock init` first, or it errors). The first
`sandbox` run prompts for `claude setup-token` if you have no credentials, runs `git init`
if the path isn't a git repo yet, and (re)attaches the session.
`sandbox` requires an `.openlock/` (run `openlock init` first, or it errors). If you have no
credentials it runs `openlock login` (the Anthropic provider imports your Claude subscription
token from an isolated `claude auth login`). The first run also `git init`s the path if it
isn't a git repo yet, and (re)attaches the session.

## Usage

Expand Down
2 changes: 1 addition & 1 deletion docs/agent-config-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Top-level keys: `version` (required, integer) plus optional `filesystem_policy`,

- endpoint keys: `host`, `port`, `ports`, `protocol`, `tls`, `enforcement`, `access`, `rules`, `allowed_ips`, `deny_rules`, `allow_encoded_slash`, `cred_inject`, `echo`, `trust_check`.
- L7 rule: `allow` with matchers `method`, `path`, `command`, `query`; `deny_rules` use the same matchers. The query matcher key is `any`.
- `cred_inject`: `provider`, `strip_headers`, `inject` (each inject entry has `header`, `from_credential`).
- `cred_inject`: `provider`, `strip_headers`, `inject` (each inject entry has `header`, `from_credential`, and an optional `value_prefix` — a literal string such as `"Bearer "` prepended to the resolved credential when composing the header).
- `trust_check`: `registry`.
- binary entry: `path` (string). A deprecated `harness` boolean is also accepted on a binary entry — legacy, unrelated to the top-level harness enum below; real policies omit it.
- `filesystem_policy`: `include_workdir`, `read_only`, `read_write`.
Expand Down
19 changes: 1 addition & 18 deletions policies/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,30 +47,14 @@ network_policies:
inject:
- header: Authorization
from_credential: ANTHROPIC_BEARER_TOKEN
value_prefix: 'Bearer '
allowed_secrets:
- ANTHROPIC_BEARER_TOKEN
opencode:
binaries:
- path: /usr/local/bin/opencode
- path: /usr/local/bin/node
endpoints:
- host: api.anthropic.com
port: 443
protocol: rest
enforcement: enforce
rules:
- allow:
method: POST
path: /v1/**
cred_inject:
provider: anthropic
strip_headers:
- Authorization
- x-api-key
- Cookie
inject:
- header: x-api-key
from_credential: ANTHROPIC_API_KEY
- host: openrouter.ai
port: 443
protocol: rest
Expand All @@ -97,7 +81,6 @@ network_policies:
method: GET
path: /**
allowed_secrets:
- ANTHROPIC_API_KEY
- OPENROUTER_BEARER_TOKEN
npm_packages:
binaries:
Expand Down
6 changes: 3 additions & 3 deletions policies/wire-proof-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# Usage:
# Terminal 1: just gateway-vm
# Terminal 2: just cli provider create --name anthropic --type claude --credential ANTHROPIC_API_KEY=sk-test-key
# Terminal 2: just cli provider create --name anthropic --type claude-oauth --credential ANTHROPIC_BEARER_TOKEN=sk-test-key
# just cli sandbox create --name wire-proof --provider anthropic \
# --policy policies/wire-proof-local.yaml -- /bin/bash
#
Expand Down Expand Up @@ -40,5 +40,5 @@ network_policies:
provider: anthropic
strip_headers: [Authorization, x-api-key, Cookie]
inject:
- { header: Authorization, from_credential: ANTHROPIC_BEARER_TOKEN }
allowed_secrets: [ANTHROPIC_BEARER_TOKEN, ANTHROPIC_AUTH_TOKEN]
- { header: Authorization, from_credential: ANTHROPIC_BEARER_TOKEN, value_prefix: "Bearer " }
allowed_secrets: [ANTHROPIC_BEARER_TOKEN]
3 changes: 3 additions & 0 deletions scripts/render-default-policies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ function harnessBlock(harness: Harness): Record<string, unknown> {
inject: ep.cred_inject.inject.map((i) => ({
header: i.header,
from_credential: i.from_credential,
// Preserve the literal prefix (e.g. "Bearer ") when present; a
// cred whose stored value carries no prefix omits the key.
...(i.value_prefix ? { value_prefix: i.value_prefix } : {}),
})),
},
}
Expand Down
9 changes: 9 additions & 0 deletions src/cli/sandbox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,13 @@ describe("sandbox flagSchema (extended)", () => {
});
expect(values.provider).toBe("openrouter");
});

it("accepts --no-attach as a boolean", () => {
const { values } = parseArgs({
args: ["--no-attach"],
options: flagSchema,
allowPositionals: true,
});
expect(values["no-attach"]).toBe(true);
});
});
2 changes: 2 additions & 0 deletions src/cli/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const flagSchema = {
harness: { type: "string" },
provider: { type: "string" },
branch: { type: "string", short: "b" },
"no-attach": { type: "boolean" },
help: { type: "boolean", short: "h" },
} as const satisfies ParseArgsOptionsConfig;

Expand All @@ -29,6 +30,7 @@ export function sandboxCmd(args: string[]): void {
harness: values.harness,
provider: values.provider,
branch: values.branch,
noAttach: values["no-attach"] === true,
}),
);
}
46 changes: 46 additions & 0 deletions src/config-core/policy/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,52 @@ describe("validateSchema", () => {
expect(errors.some((e) => e.message.includes("nope"))).toBe(true);
});

test("accepts cred_inject header with value_prefix", () => {
const errors = validateSchema({
version: 1,
network_policies: {
test: {
endpoints: [
{
host: "api.anthropic.com",
cred_inject: {
provider: "anthropic",
strip_headers: ["Authorization"],
inject: [
{
header: "Authorization",
from_credential: "ANTHROPIC_BEARER_TOKEN",
value_prefix: "Bearer ",
},
],
},
},
],
},
},
});
expect(errors).toHaveLength(0);
});

test("rejects unknown field in cred_inject header (allowlist still enforced)", () => {
const errors = validateSchema({
version: 1,
network_policies: {
test: {
endpoints: [
{
host: "x.com",
cred_inject: {
inject: [{ header: "Authorization", from_credential: "X", value_suffix: "nope" }],
},
},
],
},
},
});
expect(errors.some((e) => e.message.includes("value_suffix"))).toBe(true);
});

test("rejects missing required fields in cred_inject header", () => {
const errors = validateSchema({
version: 1,
Expand Down
3 changes: 2 additions & 1 deletion src/config-core/policy/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const L7_ALLOW_KEYS = new Set(["method", "path", "command", "query"]);
const L7_RULE_KEYS = new Set(["allow"]);
const L7_DENY_KEYS = new Set(["method", "path", "command", "query"]);

const CRED_INJECT_HEADER_KEYS = new Set(["header", "from_credential"]);
const CRED_INJECT_HEADER_KEYS = new Set(["header", "from_credential", "value_prefix"]);
const CRED_INJECT_KEYS = new Set(["provider", "strip_headers", "inject"]);
const TRUST_CHECK_KEYS = new Set(["registry"]);

Expand Down Expand Up @@ -281,6 +281,7 @@ function validateCredInjectHeader(val: unknown, path: string): ValidationError[]
for (const f of ["header", "from_credential"] as const) {
requireScalar(errors, obj, f, "string", path);
}
optionalScalar(errors, obj, "value_prefix", "string", path);
return errors;
}

Expand Down
1 change: 1 addition & 0 deletions src/config-core/policy/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
interface CredInjectHeader {
header: string;
from_credential: string;
value_prefix?: string;
}

interface CredInject {
Expand Down
5 changes: 3 additions & 2 deletions src/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ export async function _loginForTests(args: {
const id = args.providerFlag ? validateProviderId(args.providerFlag) : await args.pick(args.io);
const plugin = PROVIDERS[id];
args.io.writeStdout(`\nAuthenticating with ${plugin.displayName}...\n`);
const creds = await plugin.loginInteractive(args.io);
const result = await plugin.loginInteractive(args.io);
writeProvider(id, {
type: plugin.openshellType,
credentials: creds,
credentials: result.credentials,
created_at: new Date().toISOString(),
refresh: result.refresh,
});
args.io.writeStdout(`\nCredentials saved for provider '${id}'.\n`);
}
Loading