Environments
An Environment is an execution recipe selected when a Session opens. It can prepare a sandbox or Remote workspace, add process variables, expose governed actions, materialize scoped secrets, and clean up afterward.
Use an Environment for reproducible execution behavior. Use an Agent for durable identity and instructions, and a Remote when the workspace must live on hardware you control.
Create an Environment
name is an immutable lowercase kebab-case identifier. supportedTargets
declares whether the recipe can run in a managed sandbox, on a remote, or
both.
const environment = await skopik.envs.create({
name: 'node-ci',
displayName: 'Node CI',
description: 'Installs dependencies and runs repository checks.',
supportedTargets: ['sandbox', 'remote'],
setup: {
script: 'npm ci',
timeoutSeconds: 900,
},
teardown: {
script: 'npm run services:stop --if-present',
timeoutSeconds: 120,
},
variables: [
{ name: 'NODE_ENV', value: 'test', scope: 'execution' },
],
secrets: [],
actions: [
{
key: 'test',
name: 'Run tests',
description: 'Run the repository test suite.',
script: 'npm test',
inputSchema: { type: 'object', additionalProperties: false },
effect: 'read',
approval: 'inherit',
timeoutSeconds: 900,
},
],
})
curl "https://api.skopik.com/api/v1/envs" \
-H "Authorization: Bearer $SKOPIK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "node-ci",
"display_name": "Node CI",
"supported_targets": ["sandbox", "remote"],
"setup": { "script": "npm ci", "timeout_seconds": 900 },
"teardown": { "script": "npm run services:stop --if-present", "timeout_seconds": 120 },
"variables": [{ "name": "NODE_ENV", "value": "test", "scope": "execution" }],
"secrets": [],
"actions": [{
"key": "test",
"name": "Run tests",
"script": "npm test",
"effect": "read",
"approval": "inherit",
"timeout_seconds": 900
}]
}'
The SDK returns the Environment directly at version: 1.
Bind an Environment to a Session
Select an Environment by id or slug while opening a Session:
const session = await skopik.sessions.open({
agent: agentId,
environment: environment.environmentId,
prompt: 'Run the test suite and summarize any failures.',
permission: 'ask',
})
For a Remote target, select both:
await skopik.sessions.open({
agent: agentId,
remote: remoteId,
environment: environment.environmentId,
prompt: 'Run the test suite and fix the first failure.',
permission: 'ask',
})
The selected target must be present in supportedTargets.
Activation lifecycle
Each time Skopik allocates execution for the Session, it creates an activation from the Environment's current version. That activation keeps its captured setup, actions, and teardown even if you edit the Environment while it runs. A later activation uses the latest version.
preparing → setting_up → ready → cleaning_up → cleaned
↘ failed ↘ cleanup_failed
Inspect the safe activation state without exposing scripts or secrets:
const { activation } = await skopik.sessions.getEnvironment(session.sessionId)
console.log(activation.status, activation.version, activation.target)
Setup must succeed before workspace execution begins. Teardown is attempted after success, partial setup, cancellation, and execution failure.
Update with version safety
Environment updates replace the executable configuration and require the version you read. This prevents concurrent editors from silently overwriting one another.
const current = await skopik.envs.get(environment.environmentId)
const updated = await skopik.envs.replace(current.environmentId, {
ifVersion: current.version,
displayName: 'Node CI — strict',
description: 'Installs dependencies and runs strict repository checks.',
supportedTargets: current.supportedTargets,
setup: current.setup ?? undefined,
teardown: current.teardown ?? undefined,
variables: current.variables,
secrets: current.secrets,
actions: current.actions,
})
A stale version returns HTTP 409 with code
environment_version_conflict. Reload and deliberately reconcile the newer
configuration.
Use upsert() for rerunnable configuration keyed by immutable name:
const environment = await skopik.envs.upsert({
name: 'node-ci',
displayName: 'Node CI',
supportedTargets: ['sandbox'],
setup: { script: 'npm ci', timeoutSeconds: 900 },
teardown: { script: 'npm run services:stop --if-present', timeoutSeconds: 120 },
variables: [{ name: 'NODE_ENV', value: 'test', scope: 'execution' }],
secrets: [],
actions: [],
})
Variables
Variable names use uppercase letters, numbers, and underscores and cannot use
the reserved SKOPIK_ prefix.
| Scope | Available to |
|---|---|
execution |
Setup, teardown, workspace execution, and actions. |
actions |
Governed actions only. |
Values are injected only into the target process. They are not added to the Agent prompt.
Managed secrets for sandboxes
Environment secrets are encrypted values referenced by id. Creating,
rotating, and binding them requires an admin API key.
Create the Environment, then create its secret:
const { secret } = await skopik.envs.secrets.create(environment.environmentId, {
name: 'GITHUB_TOKEN',
description: 'Read-only token for source inspection.',
value: process.env.GITHUB_TOKEN,
allowedAgents: [agentId],
})
Bind the secret to named consumers in the next Environment version:
const current = await skopik.envs.get(environment.environmentId)
await skopik.envs.replace(current.environmentId, {
ifVersion: current.version,
displayName: current.displayName,
description: current.description ?? undefined,
supportedTargets: ['sandbox'],
setup: current.setup ?? undefined,
teardown: current.teardown ?? undefined,
variables: current.variables,
secrets: [
{
name: 'GITHUB_TOKEN',
source: { kind: 'managed', secretId: secret.secretId },
consumers: ['setup', 'action:test'],
},
],
actions: current.actions,
})
Managed values are materialized only for the declared process. They are not returned by Environment reads or written into Session transcripts, events, traces, or action results.
Rotate a value without editing the Environment binding:
await skopik.envs.secrets.rotate(
environment.environmentId,
secret.secretId,
process.env.NEW_GITHUB_TOKEN,
)
Secret aliases for Remotes
Managed secret values are never sent to a Remote. A Remote-compatible Environment references a machine-local alias instead:
const binding = {
name: 'GITHUB_TOKEN',
source: { kind: 'remote' as const, alias: 'github-readonly' },
consumers: ['setup' as const, 'action:test' as const],
}
The Remote's local secret provider decides whether to trust the exact Environment configuration and resolves the alias on the machine. With no provider or trust decision, materialization fails closed.
Governed actions
Actions are reviewed scripts with JSON input, an effect classification, an approval policy, and a timeout.
| Effect | Session behavior |
|---|---|
read |
Allowed in all permission modes. |
workspace_write |
Denied by readonly; approval-gated by ask. |
external_write |
Denied by readonly; approval-gated by ask. |
approval: 'always' requires approval even when the Session uses
permission: 'full'. approval: 'inherit' follows Session permission.
List and invoke actions from the activation snapshot:
const { data: actions } = await skopik.sessions.listEnvironmentActions(sessionId)
const { operation } = await skopik.sessions.invokeEnvironmentAction(
sessionId,
'test',
{ suite: 'integration' },
)
const latest = await skopik.sessions.getEnvironmentAction(
sessionId,
operation.operationId,
)
Action input is validated against inputSchema and supplied as JSON to the
reviewed script. Inputs are never interpolated into shell text.
List, inspect, and archive
const { data: environments, page } = await skopik.envs.list({
status: 'active',
target: 'sandbox',
limit: 50,
})
const environment = await skopik.envs.get(environments[0].environmentId)
await skopik.envs.delete(environment.environmentId)
Delete archives the Environment. It prevents new activations but does not interrupt one already in progress.
API reference
| SDK | HTTP | Purpose |
|---|---|---|
envs.list() |
GET /envs |
List Environments. |
envs.create() |
POST /envs |
Create version 1. |
envs.get() |
GET /envs/{idOrSlug} |
Read the current safe configuration. |
envs.replace() |
PUT /envs/{idOrSlug} |
Replace configuration with ifVersion. |
envs.upsert() |
Client helper | Create or replace by immutable name. |
envs.delete() |
DELETE /envs/{idOrSlug} |
Archive the Environment. |
envs.readiness() |
GET /envs/readiness |
Summarize recent setup and teardown readiness. |
envs.secrets.* |
/envs/{id}/secrets/* |
Manage encrypted Environment secrets. |