core/src/permission-rules
The permission policy pi runs under (#446, #496): ENSO_HOME/permissions.json, in
@gotgenes/pi-permission-system’s format, never a copy of another tool’s settings.
gotgenes reads its policy from one place only, <agentDir>/extensions/pi-permission-system/ config.json, and there is no setting to point it elsewhere. So preparePermissionPolicy
makes that path a symlink to the user’s file. The file stays where #446 put it, and gotgenes’
reload on change still works, since it follows the link and stats the target.
It also writes the mode agent files (permission-modes.ts) beside pi’s other agent
files, because gotgenes reads a mode’s preset from there.
⚠ gotgenes reads a file it cannot parse as an EMPTY policy, and an empty policy has no deny rules at all: every call asks. That is why an unreadable file refuses the launch here rather than being passed on. A file the user breaks while a session runs still reaches gotgenes that way; nothing here can stop that.
Only plain JSON is accepted. gotgenes also strips JavaScript-style comments before it parses, so every file this module accepts, it reads the same way. A commented file is refused here even though gotgenes would read it; that is the safe direction.
Permission mode
Section titled “Permission mode”PermissionPolicyPreparation
Section titled “PermissionPolicyPreparation”PermissionPolicyPreparation = {
catchAllAdded: readonlystring[];kind:"kept";movedAside?:string;path:string;surfaces:number; } | {kind:"seeded";movedAside?:string;path:string;piccBackup?:string;was:"absent"|"without a permission block"|"a picc rules file"; } | {kind:"unreadable";path:string;reason:string; }
Defined in: core/src/permission-rules.ts:165
What preparePermissionPolicy found and did with the user’s file.
kept: the file holds apermissionblock; it is used as it is.seeded: the file now holdsENSO_DEFAULT_PERMISSION_POLICY. It was absent, held nopermissionblock, or was a picc rules file (#496; that file is kept beside it, atpiccBackup, and its rules are not carried over).unreadable: the file is there but is not a JSON object (or is a link to nothing); nothing was written, and the caller refuses to start.
movedAside names where a regular file found at the engine’s config path was moved to (see
linkEngineConfig), so the launch log says so. catchAllAdded names the surfaces of a kept
file that got Enso’s catch-all put first (withCatchAllFirst), so the log says that too.
ENSO_DEFAULT_PERMISSION_POLICY
Section titled “ENSO_DEFAULT_PERMISSION_POLICY”
constENSO_DEFAULT_PERMISSION_POLICY:object
Defined in: core/src/permission-rules.ts:79
The policy a fresh ENSO_HOME starts with.
- Tools: reads and searches run, as do Enso’s
ask_userandweb_search; edits and writes ask; anything unnamed asks ("*"),web_fetchincluded. - Catch-alls: every map starts with Enso’s catch-all,
**(ENSO_CATCH_ALL): the one key a mode changes, and the patternautomay answer. A rule a user adds after it is theirs, and beats every mode (#498, #499). - Bash: asks, except gotgenes’ documented read-only allowlist (its “Read-Only Bash Command
Allowlist” recipe, trimmed of
less/more, which can escape to a shell). The allowlist is safe to seed because gotgenes resolves a chain to its most restrictive part and floors wrappers (sh -c,sudo,xargs) to ask. - Git: the escapes Enso’s guards do not already refuse are denied (force-push is
cc-safety-net’sgit.push-force). .envfiles: denied for every tool and every bash command, through symlinks too.- Outside the project: asks.
automode:authorizerChainnames Enso’s classifier link. The link defers in every other mode, and a user who removes the name gets prompts inauto, never fewer.
⚠ path_write is allow inside the project on purpose. gotgenes checks a bash path argument
against BOTH directions unless the command is one of its pure readers, so ask here made
git diff src/a.ts ask (probed). The cost: an allowlisted command’s redirect (cat a > b)
writes inside the project without asking. Writes outside still ask through
external_directory.
⚠ git commit * -n* is deliberately absent: it would also deny a commit whose MESSAGE contains
-n. -n right after commit is still denied.
Type Declaration
Section titled “Type Declaration”authorizerChain
Section titled “authorizerChain”
readonlyauthorizerChain: readonly ["enso-auto"]
permission
Section titled “permission”
readonlypermission:object
permission.*
Section titled “permission.*”
readonly*:"ask"='ask'
permission.ask_user
Section titled “permission.ask_user”
readonlyask_user:"allow"='allow'
permission.bash
Section titled “permission.bash”
readonlybash:object
permission.bash.**
Section titled “permission.bash.**”
readonly**:"ask"='ask'
permission.bash.cat *
Section titled “permission.bash.cat *”
readonlycat *:"allow"='allow'
permission.bash.cmp *
Section titled “permission.bash.cmp *”
readonlycmp *:"allow"='allow'
permission.bash.date
Section titled “permission.bash.date”
readonlydate:"allow"='allow'
permission.bash.df *
Section titled “permission.bash.df *”
readonlydf *:"allow"='allow'
permission.bash.diff *
Section titled “permission.bash.diff *”
readonlydiff *:"allow"='allow'
permission.bash.du *
Section titled “permission.bash.du *”
readonlydu *:"allow"='allow'
permission.bash.fd *
Section titled “permission.bash.fd *”
readonlyfd *:"allow"='allow'
permission.bash.find *
Section titled “permission.bash.find *”
readonlyfind *:"allow"='allow'
permission.bash.git blame *
Section titled “permission.bash.git blame *”
readonlygit blame *:"allow"='allow'
permission.bash.git branch
Section titled “permission.bash.git branch”
readonlygit branch:"allow"='allow'
permission.bash.git commit –no-verify*
Section titled “permission.bash.git commit –no-verify*”
readonlygit commit –no-verify*:"deny"='deny'
permission.bash.git commit -n*
Section titled “permission.bash.git commit -n*”
readonlygit commit -n*:"deny"='deny'
permission.bash.git commit * –no-verify*
Section titled “permission.bash.git commit * –no-verify*”
readonlygit commit * –no-verify*:"deny"='deny'
permission.bash.git diff
Section titled “permission.bash.git diff”
readonlygit diff:"allow"='allow'
permission.bash.git diff *
Section titled “permission.bash.git diff *”
readonlygit diff *:"allow"='allow'
permission.bash.git log
Section titled “permission.bash.git log”
readonlygit log:"allow"='allow'
permission.bash.git log *
Section titled “permission.bash.git log *”
readonlygit log *:"allow"='allow'
permission.bash.git ls-files *
Section titled “permission.bash.git ls-files *”
readonlygit ls-files *:"allow"='allow'
permission.bash.git push –delete *
Section titled “permission.bash.git push –delete *”
readonlygit push –delete *:"deny"='deny'
permission.bash.git push * –delete *
Section titled “permission.bash.git push * –delete *”
readonlygit push * –delete *:"deny"='deny'
permission.bash.git remote -v
Section titled “permission.bash.git remote -v”
readonlygit remote -v:"allow"='allow'
permission.bash.git show *
Section titled “permission.bash.git show *”
readonlygit show *:"allow"='allow'
permission.bash.git status
Section titled “permission.bash.git status”
readonlygit status:"allow"='allow'
permission.bash.git status *
Section titled “permission.bash.git status *”
readonlygit status *:"allow"='allow'
permission.bash.grep *
Section titled “permission.bash.grep *”
readonlygrep *:"allow"='allow'
permission.bash.head *
Section titled “permission.bash.head *”
readonlyhead *:"allow"='allow'
permission.bash.ls
Section titled “permission.bash.ls”
readonlyls:"allow"='allow'
permission.bash.ls *
Section titled “permission.bash.ls *”
readonlyls *:"allow"='allow'
permission.bash.pwd
Section titled “permission.bash.pwd”
readonlypwd:"allow"='allow'
permission.bash.rg *
Section titled “permission.bash.rg *”
readonlyrg *:"allow"='allow'
permission.bash.sha256sum *
Section titled “permission.bash.sha256sum *”
readonlysha256sum *:"allow"='allow'
permission.bash.stat *
Section titled “permission.bash.stat *”
readonlystat *:"allow"='allow'
permission.bash.tail *
Section titled “permission.bash.tail *”
readonlytail *:"allow"='allow'
permission.bash.tree *
Section titled “permission.bash.tree *”
readonlytree *:"allow"='allow'
permission.bash.uname *
Section titled “permission.bash.uname *”
readonlyuname *:"allow"='allow'
permission.bash.wc *
Section titled “permission.bash.wc *”
readonlywc *:"allow"='allow'
permission.bash.which *
Section titled “permission.bash.which *”
readonlywhich *:"allow"='allow'
permission.bash.whoami
Section titled “permission.bash.whoami”
readonlywhoami:"allow"='allow'
permission.edit
Section titled “permission.edit”
readonlyedit:object
permission.edit.**
Section titled “permission.edit.**”
readonly**:"ask"='ask'
permission.external_directory
Section titled “permission.external_directory”
readonlyexternal_directory:object
permission.external_directory.**
Section titled “permission.external_directory.**”
readonly**:"ask"='ask'
permission.find
Section titled “permission.find”
readonlyfind:"allow"='allow'
permission.grep
Section titled “permission.grep”
readonlygrep:"allow"='allow'
permission.ls
Section titled “permission.ls”
readonlyls:"allow"='allow'
permission.path_read
Section titled “permission.path_read”
readonlypath_read:object
permission.path_read.*.env
Section titled “permission.path_read.*.env”
readonly*.env:"deny"='deny'
permission.path_read.*.env.*
Section titled “permission.path_read.*.env.*”
readonly*.env.*:"deny"='deny'
permission.path_read.*.env.example
Section titled “permission.path_read.*.env.example”
readonly*.env.example:"allow"='allow'
permission.path_read.**
Section titled “permission.path_read.**”
readonly**:"allow"='allow'
permission.path_write
Section titled “permission.path_write”
readonlypath_write:object
permission.path_write.*.env
Section titled “permission.path_write.*.env”
readonly*.env:"deny"='deny'
permission.path_write.*.env.*
Section titled “permission.path_write.*.env.*”
readonly*.env.*:"deny"='deny'
permission.path_write.**
Section titled “permission.path_write.**”
readonly**:"allow"='allow'
permission.read
Section titled “permission.read”
readonlyread:"allow"='allow'
permission.web_search
Section titled “permission.web_search”
readonlyweb_search:"allow"='allow'
permission.write
Section titled “permission.write”
readonlywrite:object
permission.write.**
Section titled “permission.write.**”
readonly**:"ask"='ask'
describePermissionPolicy()
Section titled “describePermissionPolicy()”describePermissionPolicy(
preparation):string
Defined in: core/src/permission-rules.ts:445
The line both launch surfaces log for a preparation that did not refuse, in the same words on the web host and the terminal launcher, so a reader finds one in the logs by the other.
Parameters
Section titled “Parameters”preparation
Section titled “preparation”{ catchAllAdded: readonly string[]; kind: "kept"; movedAside?: string; path: string; surfaces: number; } | { kind: "seeded"; movedAside?: string; path: string; piccBackup?: string; was: "absent" | "without a permission block" | "a picc rules file"; }
Returns
Section titled “Returns”string
permissionEngineConfigPath()
Section titled “permissionEngineConfigPath()”permissionEngineConfigPath(
agentDir):string
Defined in: core/src/permission-rules.ts:145
Where gotgenes reads its policy, under pi’s agent directory.
Parameters
Section titled “Parameters”agentDir
Section titled “agentDir”string
Returns
Section titled “Returns”string
preparePermissionPolicy()
Section titled “preparePermissionPolicy()”preparePermissionPolicy(
policyPath,agentDir):PermissionPolicyPreparation
Defined in: core/src/permission-rules.ts:395
Make policyPath a policy gotgenes will use as it is, and put it and the mode presets where
gotgenes reads them:
- keep a file that holds a
permissionblock; - seed one that is absent, holds none, or is a picc rules file;
- refuse one that is not a JSON object, touching nothing.
Seeding keeps every other top-level key the file had, except a picc file, which is replaced
whole (its copy is kept beside it; replacePiccRulesFile names it).
Call it before the extension loads, on both launch surfaces.
Parameters
Section titled “Parameters”policyPath
Section titled “policyPath”string
agentDir
Section titled “agentDir”string
Returns
Section titled “Returns”replacePiccRulesFile()
Section titled “replacePiccRulesFile()”replacePiccRulesFile(
policyPath):string|undefined
Defined in: core/src/permission-rules.ts:269
Move a picc rules file aside and write the seed in its place, returning the backup’s path, or
undefined when the file was already gone.
⚠ TWO LAUNCHES MAY BOTH DECIDE TO MIGRATE (#497 review): the web host and the terminal, on a home
not yet migrated, each read the picc file before either moved it. So this step runs with a
decision that may be stale, and whatever it moves, moveAside never overwrites an earlier
backup: the user’s rules stay in the first one (.picc-backup), and a late launch’s copy of the
seed, or of the same rules, lands in .picc-backup.1. When the file is already gone (the other
launch moved it and has not written the seed yet), the caller reads again rather than failing.
Parameters
Section titled “Parameters”policyPath
Section titled “policyPath”string
Returns
Section titled “Returns”string | undefined
