Write your own
Bring your own content
Load scenarios and faults from your own repository, no fork needed.
docs/authoring/extensions.mdOn this page
SnowOps Labs keeps a small, documented seam so custom or private builds can plug in behaviour without forking the engine. Everything in the open repository behaves identically whether or not anything is injected, no business logic ever enters the open engine.
This package is part of the public SDK and is CODEOWNERS-locked:
pkg/extension: where content comes from (resolvers) and what happens around lifecycle phases (hooks).
The entitlement seam and the OCI/pack resolver that used to live here were removed in v2 - see ADR-0001 and ADR-0008. Content distribution is now plain directories plus
SNOWOPS_CONTENT_PATH.
Resolvers: where content comes from
type Resolver interface {
CanResolve(ref string) bool
Resolve(ctx context.Context, ref, destDir string) error
}A Chain tries each resolver in order and uses the first that reports
CanResolve. The built-in open resolvers are:
| Resolver | Handles |
|---|---|
GitResolver |
https://…, git+https://…@ref, ssh URLs - clones a shallow content snapshot and drops .git |
LocalResolver |
file://… and existing local directories - copies the tree |
DefaultResolver() returns Chain{GitResolver{}, LocalResolver{}}.
To add a source, implement the interface and put it ahead of the chain:
chain := append(extension.Chain{myResolver{}}, extension.DefaultResolver()...)GitResolver takes a GitRunner so tests can inject a stub instead of shelling
out to real git.
Hooks: around lifecycle phases
type Hooks interface {
PreStage(ctx context.Context, ev Event) error
PostStage(ctx context.Context, ev Event) error
}DefaultHooks() is a no-op. A Pre* hook returning an error aborts the phase,
so a custom build can enforce policy without the engine knowing the policy
exists. Hooks receive an Event naming the scenario and stage.
Rules for anything built on these seams
- The open engine must behave identically with the default implementations.
- Custom logic lives in a separate repository and is injected at construction, never merged into the open engine.
- Changes to these interfaces follow the SDK & Schema Stability Policy.