skopik

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,
		},
	],
})

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.