Scoped brokers
Project-environment callbacks receive broker objects in ProviderCallContext.
Every call is checked against the extension, provider, profile, operation, deadline, and
cancellation scope that Terminay issued.
Provider call context
| Member | Meaning |
|---|---|
deadlineAt | Host-assigned absolute ISO-8601 deadline. Stop work before it expires. |
signal | Cancellation signal with aborted and throwIfAborted(). |
idempotencyKey? | Stable retry key for retryable mutations. |
expectedRevision? | Optimistic-concurrency revision for an existing state mutation. |
profiles, secrets, sshAgent, dependencies | Scoped broker surfaces described below. |
Profiles and secrets
async function verifyProfile(context, profileId) {
const profile = await context.profiles.get(profileId);
return context.secrets.withValue(
{ profileId, fieldId: 'token', purpose: 'verify API access' },
async (token) => {
// Use token only here. Do not log, return, or store it.
return verifyWithService(profile.values.endpoint, token);
},
);
}| Broker | Method and result |
|---|---|
profiles | get(profileId) returns an owned provider profile: ID, provider ID, non-secret values, declared secret field IDs, and revision. |
secrets | withValue({ profileId, fieldId, purpose }, use) passes an owned secret as Uint8Array only to use. |
Secret bytes are transient and zeroized after the callback. Do not retain, return, log, serialize, display, or put them in provider state. A secret broker cannot enumerate the vault or resolve another extension’s binding.
SSH agent
| Method | Input and result |
|---|---|
listIdentities(scope) | { profileId, purpose: 'ssh-user-authentication' } → bounded public identity metadata: opaque ID, algorithm, public key, fingerprint, and optional comment. |
sign(request) | The same scope plus identity ID, algorithm, and bounded authentication challenge → algorithm and signature bytes. |
Identity IDs are opaque; the API does not reveal a keychain, agent socket, private-key path,
or private key. Both methods require ssh-agent:use.
Provider dependencies
const result = await context.dependencies.call(
{ providerId: 'ssh', operation: 'open-environment', payload: { host: 'prod' } },
{ deadlineAt: context.deadlineAt, signal: context.signal },
);
Declare a compatible extension in extensionDependencies, add the
provider:depend permission, and call only a target provider’s manifest-declared
operation. A request contains providerId, operation, and JSON
payload; the result is JSON.
The target handler receives host-authenticated caller identity, the request’s operation and payload, deadline/cancellation/idempotency/revision fields, and only a target-owned vault. It never receives the caller’s profiles, secrets, SSH agent, or filesystem scope.
Target-owned dependency vault
| Method | Meaning |
|---|---|
vault.put() | Atomically create or replace a target-owned binding from binding key, purpose, bytes, idempotency key, and optional expected revision. Returns an opaque binding and revision. |
vault.withSecret() | Supply an opaque binding and purpose; secret bytes exist only inside its callback and are zeroized afterward. |
vault.remove() | Delete a binding with an idempotency key and optional expected revision. Returns deleted or pending. |
A binding is durable but opaque and scoped to the extension installation and target provider. It is not a vault path or global secret ID.
Security boundary
Brokers are host-mediated, typed IPC—not direct server internals. They enforce ownership, declared dependencies, operation allowlists, cancellation, and bounded data transfer for each request.