Skip to main content
N4M3Z

forge-tlp

by N4M3Zv0.2.0

TLP file access control — RED/AMBER/GREEN/CLEAR classification, safe-read, blind-metadata. USE WHEN accessing protected files or configuring file access policies.

Installation guide →
1 skill GitHub

Documentation

# context-tlp

Traffic Light Protocol (TLP) file access control plugin for Claude Code.

## What It Does

Enforces sensitivity-based file access policies following the [Traffic Light Protocol](https://www.cisa.gov/news-events/news/traffic-light-protocol-tlp-definitions-and-usage).
Files are classified as `RED` (blocked), `AMBER` (requires approval), `GREEN` (open), or `CLEAR` (public) using a `.tlp` config file (YAML syntax) or file metadata.
`AMBER` reads go through `safe-read`, which strips inline `#tlp/red` sections before the AI sees the content.

The convention is based on Obsidian so it can be used directly with your Obsidian vault provided you use the `tlp:` property or the `#tlp/red` tag to redact files.

## Examples

### `.tlp` config

```yaml
RED:
  - "*.pdf"
  - "Resources/Contacts/**"

AMBER:
  - "Resources/Journals/**"

GREEN:
  - "Topics/**"

CLEAR:
  - ".tlp"
  - "CLAUDE.md"
```

### `tlp-guard` — blocking RED files

`tlp-guard` is a [PreToolUse hook](https://docs.anthropic.com/en/docs/claude-code/hooks#pretooluse) 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:

```bash
$ 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:

```bash
$ 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:**

```markdown
---
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 [email protected] #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 [email protected] [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](https://github.com/gitleaks/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](https://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 context-tlp@forge-plugins
```

### Local testing

```bash
claude --plugin-dir /path/to/Plugins/context-tlp
```

### Post-install

Whitelist the CLI tools in your project or global `settings.local.json`:

```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](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:

```yaml
---
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

```bash
# 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

- [Claude Code Hooks — PreToolUse](https://docs.anthropic.com/en/docs/claude-code/hooks#pretooluse)
- [gitleaks](https://github.com/gitleaks/gitleaks) — secret detection patterns used by `safe-read`

## License

[EUPL-1.2](LICENSE)