aux4
Documentation
HomeGetting StartedInstallationIDEsVariablesExecutorsFunctionsConfigurationCommandsHooksSecurityRenderTestingPackage ManagerPackagingGitHub ActionsDocker

Security Policy

When several packages are installed into one CLI — locally, or in a cloud deployment that bundles many packages — you often want to expose only a curated subset of commands while keeping the rest available as internal building blocks. The security policy controls exactly this.

It answers two questions about every command:

  • Callability — can it be invoked directly? (allow / ask / deny)
  • Visibility — is it listed in help and autocomplete? (a denied command is hidden)

A key property: deny blocks direct invocation only. A command that is exposed can still call a denied command from its own execute steps, so denied commands remain usable building blocks rather than dead ends.

A runtime decision

The policy is imposed by whoever runs the CLI. It is never declared inside a package — a package cannot grant itself exposure. This is what makes it a real boundary: in the cloud the platform sets the policy and the running package cannot see or change it.

1config: 2 security: 3 deny: ["*"] # nothing is directly callable... 4 allow: ["db *"] # ...except commands under the db profile 5 ask: ["deploy *"] # prompt before running (denied when non-interactive)

Pattern notation

Patterns are globs matched against the command path as typed after aux4, space-joined and without the aux4 prefix:

PatternMatches
*everything
db *every command under the db profile, at any depth
db queryexactly that one command
ai agent config *everything under the nested ai agent config path

* matches any run of characters, spaces included.

How a decision is made

Most specific match wins. The pattern with the fewest wildcards (longest literal prefix) decides; on a tie, deny beats ask beats allow (fail closed).

This gives two natural idioms with the same mechanism:

1# allow-list: expose only db 2security: 3 deny: ["*"] 4 allow: ["db *"] 5 6# deny-list: expose everything except one command 7security: 8 deny: ["db secret"]
  • deny: ["*"] + allow: ["db *"] → only db commands are callable; everything else is denied and hidden.
  • deny: ["db secret"] + allow: ["db *"] → db is exposed, but db secret is hidden — the exact deny is more specific than the db * allow.

Routers stay navigable. A profile that leads to an allowed command is kept visible so you can reach it. With deny: ["*"] + allow: ["grp *"], the grp router is still listed and aux4 grp one runs, while unrelated siblings are hidden.

deny hides

A denied command is:

  • left out of aux4 --help, aux4 --help --json, and shell autocomplete, and
  • reported as Command not found if invoked directly.

There is no separate "hidden" list — hiding follows from being denied. (The command-level private flag remains available for the separate case of a command that is callable but not listed.)

Sources and precedence

A policy can be tightened but never loosened by a lower-trust source — otherwise the boundary would be worthless. So security is resolved with the precedence env → config → param, the reverse of aux4's normal "a param overrides config" rule.

SourceHow to set itStrength
AUX4_SECURITY environment variablea JSON stringAuthoritative. Inherited by subprocess shell-outs and unreachable from a param — the real boundary, used in the cloud.
Config filethe security: key, read with --configFile / --configProtects the top-level call, but does not propagate to a subprocess shell-out. Best for local use.
Parameter--security '{...}' or --security.allow "db *"Only consulted when neither of the above defines a policy.
1# expose only db, imposed from the environment 2export AUX4_SECURITY='{"deny":["*"],"allow":["db *"]}' 3 4aux4 db query # runs 5aux4 other # Command not found 6 7# a param cannot loosen an imposed policy 8aux4 other --security '{"allow":["*"]}' # still Command not found

The security parameter is reserved: it never reaches the command being run and never forwards through value(*) / object(*), so a package can neither read nor re-broadcast the policy it is running under.

Internal calls and the pipe form

Internal command-to-command calls are exempt from the block, so an exposed command keeps working even when it depends on denied commands:

1{ 2 "name": "report", 3 "execute": ["aux4 db query --sql '...'", "echo done"] 4}

Here report may be allowed while db query is denied — report still runs.

This exemption applies to the in-process form (aux4 other with no shell metacharacters). A call that is piped or redirected (aux4 other | jq) shells out to a fresh top-level process and is re-evaluated by the policy. Keep internal calls in the plain form to stay exempt.