Skip to content

N4M3Z/forge-tlp

Repository files navigation

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:

---
tlp: RED
---

The effective level is the more restrictive of the path-based and frontmatter-based classification. A file can escalate (GREEN path + RED frontmatter = RED) but never downgrade (AMBER path + GREEN frontmatter = AMBER).

Fail-closed behavior

If .tlp exists but cannot be read (permissions, corruption), all files in that vault are treated as RED and access is blocked until the config is fixed. This prevents accidental exposure from a broken config.

Files outside any vault (no .tlp in any parent directory) are not affected by the hook.

Architecture

Read request
  → tlp-guard-wrapper.sh (builds if needed)
    → tlp-guard binary
      → walks up to .tlp config
      → classifies file (path pattern + frontmatter override)
      → RED: block (exit 2)
      → AMBER + Read: block, suggest safe-read
      → AMBER + Edit/Write: allow + warn
      → GREEN/CLEAR: allow

Hooks use bash scripts. Windows users need WSL or Git Bash. Claude Code plugin hooks don't currently support .bat/.ps1 natively.

safe-read also checks TLP classification and refuses RED files.

Development

# Run all tests (unit + integration)
cargo test

# Check for warnings
cargo clippy -- -D warnings

# Build release binaries
cargo build --release

# Format code
cargo fmt

Project structure

src/
  lib.rs                # Library crate (re-exports modules)
  tlp/
    mod.rs              # TLP enum, classify(), pattern matching
    tests.rs            # Unit tests
  vault/
    mod.rs              # Vault discovery (walk up to .tlp)
    tests.rs            # Unit tests
  redact/
    mod.rs              # TLP section redaction + secret detection
    tests.rs            # Unit tests
  frontmatter/
    mod.rs              # YAML frontmatter get/set, .md file listing
    tests.rs            # Unit tests
  bin/
    tlp-guard.rs        # PreToolUse hook binary
    safe-read.rs        # Redacting file reader binary
    blind-metadata.rs   # Frontmatter bulk operations binary
tests/
  fixtures/
    configs/            # .tlp config fixtures
    content/            # .md content fixtures
  tlp_guard.rs          # Integration tests for tlp-guard
  safe_read.rs          # Integration tests for safe-read
  blind_metadata.rs     # Integration tests for blind-metadata

References

License

EUPL-1.2

Path Resolution

Use Core/bin/paths.sh as the shared cross-platform path contract.

From the repository root:

eval "$(bash Core/bin/paths.sh)"

Then use the exported values (FORGE_ROOT, FORGE_USER_ROOT, SAFE_READ_CMD, SAFE_WRITE_CMD, memory paths, journal path, backlog path) instead of re-implementing path/env resolution inside skills.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •