forge-tlp
Traffic Light Protocol (TLP) file access control for AI coding tools. Enforces sensitivity-based access policies at the tool level — the AI never sees content it shouldn't.
CLAUDE.md and AGENTS.md are autogenerated by /Init (forge-init.sh). Do not edit directly — run /Update (forge-update.sh) to regenerate.
Layer
Behaviour — part of forge-core's three-layer architecture (Identity / Behaviour / Knowledge). Enforced via PreToolUse hook on every Read, Edit, and Write call.
What It Does
Classifies files as RED (blocked), AMBER (requires approval), GREEN (open), or CLEAR (public) using a .tlp config file (YAML syntax) or frontmatter metadata. AMBER reads go through safe-read, which strips inline #tlp/red sections before the AI sees the content.
The convention is Obsidian-native — use the tlp: frontmatter property or the #tlp/red tag to redact sensitive sections inline.
Skills
| Skill | Purpose |
|---|
TLP | TLP classification rules and access conventions |
Examples
.tlp config
RED:
- "*.pdf"
- "Resources/Contacts/**"
AMBER:
- "Resources/Journals/**"
GREEN:
- "Topics/**"
CLEAR:
- ".tlp"
- "CLAUDE.md"
tlp-guard — blocking RED files
tlp-guard is a PreToolUse hook that intercepts Read, Edit, and Write tool calls before they execute. Claude Code pipes the tool call as JSON on stdin; the hook resolves the file's TLP level and either allows or blocks it.
Given the .tlp config above, Resources/Contacts/** is RED. When Claude tries to read a contact file, the hook blocks it:
$ echo '{"tool_name":"Read","tool_input":{"file_path":"/vault/Resources/Contacts/john.md"}}' \
| tlp-guard
TLP:RED — access blocked for: Resources/Contacts/john.md
$ echo $?
2
Write and Edit calls are blocked the same way — no tool call targeting a RED file gets through:
$ echo '{"tool_name":"Edit","tool_input":{"file_path":"/vault/Resources/Contacts/john.md","old_string":"...","new_string":"..."}}' \
| tlp-guard
TLP:RED — access blocked for: Resources/Contacts/john.md
$ echo $?
2
Exit code 2 tells Claude Code to deny the tool call. The AI never sees the file content.
Inline #tlp/red redaction
For AMBER files that contain sensitive sections, safe-read strips #tlp/red regions before the AI sees the content.
Source file:
---
tlp: AMBER
---
# Meeting Notes
Discussed project timeline with the team.
#tlp/red
Salary negotiations: offered $150k, counter at $170k.
#tlp/amber
Next steps: finalize budget by Friday.
Contact Alice at alice@example.com #tlp/red (personal: 555-0123) #tlp/amber for details.
safe-read output:
---
tlp: AMBER
---
# Meeting Notes
Discussed project timeline with the team.
[REDACTED]
Next steps: finalize budget by Friday.
Contact Alice at alice@example.com [REDACTED] for details.
Block-mode #tlp/red sections are replaced with [REDACTED]. Inline #tlp/red markers redact to the next #tlp/* boundary tag or end of line. Any detected secrets (API keys, tokens, credentials) are replaced with [SECRET REDACTED] using patterns sourced from gitleaks.
Components
- tlp-guard (hook) — PreToolUse hook that intercepts Read/Edit/Write
- safe-read (CLI) — Reads files with inline
#tlp/red redaction + secret detection
- blind-metadata (CLI) — Bulk YAML frontmatter operations
Requirements
- Rust toolchain (rustup.rs) — binaries build on first use
- A
.tlp file at the root of each directory tree to protect
Installation
From marketplace
/plugin marketplace add N4M3Z/forge-plugins
/plugin install forge-tlp@forge-plugins
Local testing
claude --plugin-dir /path/to/Modules/forge-tlp
Post-install
Whitelist the CLI tools in your project or global settings.local.json:
{
"permissions": {
"allow": [
"Bash(<plugin-path>/bin/safe-read:*)",
"Bash(<plugin-path>/bin/blind-metadata:*)"
]
}
}
Configuration
Create a .tlp file at your directory root. See examples/tlp.example.yaml.
Pattern syntax
Patterns are listed under level headers (RED:, AMBER:, GREEN:, CLEAR:) as quoted strings with a - prefix:
| Pattern | Matches | Example |
|---|
*.ext | Any file with that extension, anywhere in the tree | "*.pdf" matches docs/report.pdf |
dir/** | All files under a directory (recursive) | "Contacts/**" matches Contacts/john.md |
exact/path.md | Exact relative path only | "README.md" matches only README.md at the root |
First match wins. Files not matched by any pattern default to AMBER.
Frontmatter override
Files can escalate their own protection level via a tlp: field in YAML frontmatter: