Documentation
¶
Overview ¶
Package scaffold downloads a starting set of files from this repository's main branch and places them in a project. It never overwrites, and it never transforms what it fetches: what is on main is what lands on disk.
Index ¶
Constants ¶
const BaseURL = "https://raw.githubusercontent.com/ynny-github/agent-sandbox/main/"
BaseURL is where every asset is fetched from. It is pinned to main on purpose, not to the binary's own version: init then works the moment a template lands on main, and the profiles are validated against the operator's own nono before anything is written.
Variables ¶
var Assets = []Asset{ {Source: "templates/minimal/agent-sandbox.toml", Dest: "agent-sandbox.toml"}, {Source: "templates/minimal/command-profile.json", Dest: "command-profile.json", Profile: true}, {Source: "templates/minimal/claude-profile.json", Dest: "claude-profile.json", Profile: true}, { Source: ".claude/skills/growing-a-nono-profile/SKILL.md", Dest: ".claude/skills/growing-a-nono-profile/SKILL.md", }, { Source: ".claude/skills/verifying-nono-sandbox-claims/SKILL.md", Dest: ".claude/skills/verifying-nono-sandbox-claims/SKILL.md", }, }
Assets is the fixed set init places. The skills are seeded once and never updated: the project owns them from that point on, which is why there is no --force and no refresh path.
Functions ¶
func Validate ¶
Validate hands every fetched profile to `nono profile validate` before any file is written. What it covers is narrow: the templates are validated on main before merge and init does not transform them, so the case left is the operator's nono differing from the developer's. It is kept because it costs one subprocess call against a dependency agent-sandbox already requires.
It is not a security check. validate inspects syntax, field names, enum values and group references; it does not look at what a profile grants.
Warnings are returned rather than treated as failures. The command template raises [allow_all_network] on ssh by design.
func Write ¶
Write places every fetched asset under dir. A destination that already exists is skipped, never rewritten: the skills are seeded once and the project owns them afterwards, and a profile a user has grown must not be replaced by an upgrade.
The existence check and the write are one atomic operation (O_WRONLY|O_CREATE|O_EXCL) rather than a stat followed by a separate write. os.Stat follows symlinks, so a dangling symlink at dest would read as fs.ErrNotExist and a subsequent os.WriteFile would then write through it -- potentially outside the project directory entirely. O_EXCL refuses to open through an existing entry, symlink or not, and closes that stat/write race in the process.
Returned paths are Asset.Dest values, in Assets order.
Types ¶
type Asset ¶
type Asset struct {
// Source is the path under the repository root, appended to BaseURL.
Source string
// Dest is the path relative to the project directory.
Dest string
// Profile marks a nono profile. Profiles are handed to
// `nono profile validate` before any file is written.
Profile bool
}
Asset is one file init places.
type Fetched ¶
type Fetched struct {
Asset Asset
URL string
Body []byte
// ETag is the file's content hash as raw.githubusercontent reports it.
// It comes free with the response, unlike the branch head sha, which
// would cost a second request against an unauthenticated rate limit.
ETag string
}
Fetched is one asset's bytes together with what init reports about their origin. Profiles are a security boundary, so where each byte came from is printed rather than left implicit.