Sandbox Profile¶
sandbox_profile
¶
Sandbox profiles: what a target repo needs inside the agent sandbox.
A sandbox profile describes the toolchains, egress presets, in-sandbox setup and validation commands, declared skips, environment variables, resources and download exclusions for one target repo. The same shape is used in two places:
- Central profiles come from reviewed CI configuration (for example
autofix.json) and may set everything, including raw egress endpoints,
resourcesandoverlay. - Overlay profiles come from the target repo (the
sandbox:section of.agentic-ci/config.ymlon its base branch). A repo file must not be able to break the run or widen what central config allows, so fields an overlay may not set are dropped with a warning instead of failing the parse.
:func:parse_profile validates raw YAML/JSON data into an immutable
:class:SandboxProfile, :func:merge_profiles combines a central profile
with an overlay, and :func:profile_to_dict / :func:profile_hash serialize
one. SkillConfig.sandbox_profile carries the result to the backend.
Every field takes effect in OpenShellBackend: it sizes the sandbox with
resources, opens the egress presets and raw endpoints
(agentic_ci.backends.openshell.policy holds the preset endpoints),
provisions the toolchains from :mod:agentic_ci.toolchains, runs the
setup steps in the sandbox before the agent and the validate commands
after it, records the skips, exports env and removes the
discard_before_download paths (agentic_ci.backends.openshell.steps).
Error and warning messages name the field path (for example
validate[2].kind) and the rule broken. They never include env values
or run strings, which can hold anything.
KNOWN_TOOLCHAINS = frozenset(TOOLCHAIN_CATALOG)
module-attribute
¶
Toolchain names a profile may request: the entries of agentic_ci.toolchains.CATALOG.
KNOWN_EGRESS_PRESETS = frozenset({'crates', 'github-release-assets', 'goproxy', 'npm', 'pypi'})
module-attribute
¶
Egress preset names a profile may request.
The endpoints live in agentic_ci.backends.openshell.policy.EGRESS_PRESETS,
whose names a test keeps equal to this set; this module stays backend-neutral.
DEFAULT_OVERLAY_ALLOWED_PRESETS = frozenset({'crates', 'goproxy', 'npm', 'pypi'})
module-attribute
¶
Egress presets a repo overlay may add unless the caller allows others.
CREDENTIAL_ENDPOINT_OPTIONS = frozenset({'allow-uninspected-credentials', 'request-body-credential-rewrite', 'websocket-credential-rewrite'})
module-attribute
¶
Raw endpoint options that let a rule carry or rewrite provider credentials.
A raw endpoint may also carry allowed-ip=IPV4[/BITS] options. The OpenShell
backend never opens an endpoint with a credential option to the setup shim.
FrozenMap(items=())
¶
Bases: Mapping[str, str]
Immutable, hashable str -> str mapping with keys kept in sorted order.
Source code in src/agentic_ci/sandbox_profile.py
SetupStep(name, run, timeout=DEFAULT_STEP_TIMEOUT)
dataclass
¶
A command run inside the sandbox before the agent starts.
ValidateStep(name, kind, run, timeout=DEFAULT_STEP_TIMEOUT)
dataclass
¶
A validation command; kind is one of lint, build, test, generated.
Skip(match, reason)
dataclass
¶
A check the sandbox can never run, recorded instead of attempted.
Resources(memory=None, cpu=None, gpu=None)
dataclass
¶
Sandbox size. None leaves that limit to OpenShell's default.
SandboxProfile(toolchains=FrozenMap(), egress=(), raw_egress=(), setup=(), validate=(), skips=(), env=FrozenMap(), resources=None, discard_before_download=(), overlay='merge')
dataclass
¶
A validated, immutable sandbox profile.
Build one with :func:parse_profile. Mappings passed to the constructor
are frozen and sequences turned into tuples, so a profile cannot change
after it is created. A Resources with no limit set becomes None,
so equivalent profiles compare and hash the same.
ParsedProfile(profile, warnings=())
dataclass
¶
Result of :func:parse_profile: the profile and any non-fatal warnings.
MergedProfile(profile, warnings=())
dataclass
¶
Result of :func:merge_profiles: the effective profile and merge warnings.
SandboxProfileError(path, rule)
¶
is_raw_endpoint(entry)
¶
Return whether entry is a raw endpoint a central profile's egress accepts.
The same host:port:access[:protocol[:enforcement[:options]]] grammar
:func:parse_profile applies, for code that builds policy from endpoint
strings and must not keep a second copy of the rules.
Source code in src/agentic_ci/sandbox_profile.py
parse_profile(data, *, source)
¶
Validate raw profile data (the YAML/JSON shape) into a :class:SandboxProfile.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
object
|
The profile mapping, as loaded from JSON or YAML. A missing
( |
required |
source
|
Source
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
ParsedProfile
|
class: |
|
most |
ParsedProfile
|
data: |
ParsedProfile
|
yields no warnings: anything wrong raises. |
Raises:
| Type | Description |
|---|---|
SandboxProfileError
|
The data breaks a rule. For an overlay, unknown
keys, unknown toolchains and egress presets, rejected |
Rules worth knowing when writing a profile:
- Quote toolchain versions (
go: "1.20"). Integers are accepted and converted, but an unquoted decimal is rejected because YAML has already turned1.20into1.2. envvalues must be strings. Integers are converted withstr(); booleans, decimals, null, lists and mappings are rejected.egressmixes preset names and rawhost:port:accessendpoints; an entry containing:is a raw endpoint.- Each list or mapping holds at most :data:
MAX_ENTRIESentries.
Source code in src/agentic_ci/sandbox_profile.py
merge_profiles(central, overlay, *, overlay_allowed_presets=DEFAULT_OVERLAY_ALLOWED_PRESETS)
¶
Combine a central profile with a repo overlay.
Central scalars and map keys win (toolchains, env, resources,
overlay). Lists (setup, validate, skips, egress,
discard_before_download) are concatenated central first and
de-duplicated; an overlay step whose name the central list already uses
is dropped. An overlay skip is dropped when the central profile has a
skip with the same match, or when its match and the name or run
of a central validate step contain one another (ignoring case), so a
repo cannot skip validation that central configuration requires. Overlay egress presets outside
overlay_allowed_presets, overlay raw endpoints and overlay
resources are dropped. A central overlay: ignore drops the
overlay entirely.
The overlay is held to the same envelope even when it was built directly
instead of by :func:parse_profile: unknown toolchains, bad toolchain
versions, rejected env names and discard_before_download paths that
are absolute, contain .. or name the workdir or .git are dropped
with a warning.
An overlay without a central profile is merged into a default envelope:
overlay: merge, no resources and no raw egress.
Returns:
| Type | Description |
|---|---|
MergedProfile | None
|
|
MergedProfile | None
|
with the effective profile and any warnings (at most |
MergedProfile | None
|
data: |
MergedProfile | None
|
overlay that |
Source code in src/agentic_ci/sandbox_profile.py
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 | |
profile_to_dict(profile)
¶
Serialize profile to the shape :func:parse_profile accepts.
Preset names come before raw endpoints in egress. resources is
omitted when unset, as are its unset members.
Source code in src/agentic_ci/sandbox_profile.py
profile_hash(profile)
¶
Return the sha256 hex digest of profile's canonical JSON form.