Safety profiles build a dedicated gog binary with an embedded command policy.
Use them when gog is available to an agent, CI job, sandbox, or other caller
that should not be able to change its own command permissions at runtime.
Runtime guards such as --enable-commands, --disable-commands, and
--gmail-no-send are still useful for normal scripting. A baked safety profile is
stronger: the policy is compiled into the binary and cannot be changed with
flags, environment variables, config files, or shell arguments.
Build an agent-safe binary:
./build-safe.sh safety-profiles/agent-safe.yaml -o bin/gog-agent-safeBuild a read-only binary:
./build-safe.sh safety-profiles/readonly.yaml -o bin/gog-readonlyUse the built binary exactly like gog:
bin/gog-agent-safe gmail search 'from:me newer_than:7d'
bin/gog-agent-safe gmail drafts create --to you@example.com --subject "Review" --body "Draft only"
bin/gog-agent-safe gmail drafts send draft-idThe final command fails before the Gmail send handler runs:
command "gmail drafts send" is blocked by baked safety profile "agent-safe"
build-safe.sh performs a normal Go build with one extra generated file:
- Validates the YAML profile.
- Generates
internal/cmd/safety_profile_baked_gen.gowith the profile content. - Builds with
-tags safety_profile. - Runs the built binary with
--versionas a smoke test. - Deletes the generated file on exit.
Normal go build does not include a profile, so the stock gog binary is
unchanged.
At runtime, gog parses the command with Kong first. After parsing and before
any command handler or Google API call, it checks the baked profile:
- Explicit deny rules win.
- Allow rules permit matching commands.
- If the profile has allow rules, everything not allowed is blocked.
That means a caller cannot re-enable a blocked baked command:
bin/gog-readonly --enable-commands gmail.send gmail send \
--to a@example.com --subject Test --body TestThe command still fails because the baked policy is checked before runtime allowlists.
The generator emits the allow and deny rule sets as switch statements on the
FNV-64a hash of each dotted command path, not as raw YAML. The compiled rule
table never contains the rule strings themselves, so to re-enable a blocked
command an attacker has to patch compiled machine code rather than flip ASCII
bytes in a YAML blob; the cost goes from a one-line sed invocation to
disassembly-level work.
Note that command names may still appear in the binary from unrelated metadata
(API URLs, error message format strings, Kong help text). What this hardening
guarantees is that the rule set itself is no longer a contiguous, patchable
string. The profile name (e.g. agent-safe) is also embedded as a constant so
error messages can reference it.
safety-profiles/agent-safe.yaml
Allows reading, searching, drafting, labeling, archiving, organizing files, and other low-risk recoverable actions. Blocks sends, deletes, sharing changes, admin operations, and auth writes.
Good for:
- inbox triage agents
- draft reply generation
- summarization/reporting jobs that may organize labels or files
- workflows where a human should review before anything is sent
safety-profiles/readonly.yaml
Allows read/list/search/get style commands only. Blocks mutations, sends, deletes, sharing changes, auth writes, and local config writes.
Good for:
- reporting
- audits
- monitoring
- read-only agent context gathering
safety-profiles/full.yaml
Allows everything. This is mostly useful for smoke testing the build path or for
creating a -safe binary with the same command surface as stock gog.
Profiles are YAML maps that mirror command paths:
name: agent-safe
gmail:
search: true
send: false
drafts:
create: true
send: false
aliases:
send: falseRules:
trueallows a command path.falseblocks a command path.- blocked rules override allowed parent rules.
- unlisted commands are blocked when the profile has any allow rules.
- command names are written as dot paths internally, such as
gmail.drafts.create. aliases:controls root shortcuts such assend,ls,search, andupload.
Parent rules are prefix matches. For example, drive: true allows every drive
subcommand unless a child is explicitly blocked. For restrictive profiles, prefer
listing leaf commands so a parent allow does not accidentally include future
mutating subcommands:
gmail:
messages:
search: true
modify: falseUse readonly when the caller should never change Google or local gog state.
Use agent-safe when the caller may prepare work but should not perform
externally visible or hard-to-reverse actions. For example, it may create a Gmail
draft but cannot send it.
Use a custom profile when the preset is too broad or too narrow:
cp safety-profiles/readonly.yaml /tmp/my-agent.yaml
editor /tmp/my-agent.yaml
./build-safe.sh /tmp/my-agent.yaml -o bin/gog-my-agentBuild and smoke test:
./build-safe.sh safety-profiles/readonly.yaml -o gog-readonly
./gog-readonly versionCheck blocked commands:
./gog-readonly gmail messages modify msg-1 --add Label_1
./gog-readonly calendar alias set work abc123@group.calendar.google.com
./gog-readonly --enable-commands gmail.send gmail send \
--to a@example.com --subject Test --body TestEach should fail with exit code 2 before any handler or Google API call runs.
Check allowed commands:
./gog-readonly gmail search 'newer_than:1d'
./gog-readonly auth servicesSafety-profiled binaries filter help and schema output to the baked profile.
Blocked commands are not listed in parent help menus or gog schema output.
For example, agent-safe shows gmail drafts create but not gmail drafts send.
If you ask for help for a blocked leaf command directly, the binary prints the
same baked-profile block message instead of the command documentation.
Help and schema filtering are usability layers for humans and tool-discovering agents. The security boundary remains the pre-execution profile check: blocked commands fail before any command handler or Google API call runs.
Safety profiles also do not replace OAuth scopes, account separation, or Google Workspace policy. Use the narrowest practical OAuth scopes and account access, then use a baked profile as an additional local execution guard.