Skip to content

feat(security): platform guardrails overlay — most-restrictive merge over agent guardrails.json; remove unused DB mode #284

Description

@initializ-mk

Goal

Let a platform operator further restrict an agent's guardrails.json — tighten it, never loosen it — the same one-way ratchet the capability layers (platform_policy.yaml, system/user/workspace) already apply to tools/egress/models/channels.

Two parts:

  1. Add a platform guardrails overlay merged most-restrictively over the agent's guardrails.json.
  2. Remove the DB-mode guardrails construct entirely — it was never used and is the wrong model (wholesale replacement, not a further-restrict merge).

Part 1 — Platform guardrails overlay

Current state (the gap)

Two governance systems exist and don't meet:

  • Platform policy layers (forge-core/security/platform_policy*.go) restrict the capability surface (tools, egress, models, channels, counts). They know nothing about guardrails.json content.
  • guardrails.json (models.StructuredGuardrails from github.com/initializ/guardrails) is loaded per-agent from the workdir (forge-cli/runtime/guardrails_loader.go: LoadGuardrailsJSON). Nothing lets the platform layer on top of it to tighten it.

So today the platform can't say "whatever the agent declared, also force commandInjection to block and add these secret-redaction rules." That capability doesn't exist.

What guardrails.json provides (the surface to ratchet)

StructuredGuardrails sections: pii, moderation, security (jailbreak / promptInjection / sqlInjection / commandInjection + customPatterns), urlFilter, customRules, approvalGates, nsfwText, hallucination, skillConstraints, gateConfig.

Proposed merge semantics (platform can only tighten)

Field Ratchet rule
gateConfig.* boolean OR — platform can force a gate ON, never OFF
detections (pii, security.*, nsfwText, moderation, hallucination) force enabled=true; take most-severe action (warn < mask < block); take lower threshold (more sensitive)
customRules.rules, security.customPatterns, customRules.hardConstraints/softConstraints union — platform rules always added; agent can't delete them
urlFilter union denylist; intersect allowlist; force enabled
approvalGates union — platform can add required-approval conditions
skillConstraints union blockedSkills; intersect allowedSkills

Implementation sketch

  • MergeGuardrails(agent *models.StructuredGuardrails, platform *models.StructuredGuardrails) (*models.StructuredGuardrails, []GuardrailTightening) in forge-core/security, pure + unit-tested, returning which layer tightened what for audit.
  • Load the platform guardrails overlay from the SAME layer mechanism as platform_policy_layers.go (system → user → workspace) so operators author it alongside the capability policy. Union across layers is itself most-restrictive.
  • Wire into BuildGuardrailChecker (forge-cli/runtime/guardrails_loader.go) so the effective StructuredGuardrails = merge(agent file, platform overlay) before the engine is built.
  • Audit each tightening (mirror platform_policy_enforce.go's violation attribution) so operators see "commandInjection action raised warn→block by workspace layer."
  • Define a total order for action severity (warn < mask < block) and thresholds (lower = stricter) once, shared by the merge.

Acceptance

  • Platform overlay can force-enable gates/detections, raise actions, lower thresholds, and union rule/denylist/blocked-skill sets over an agent's guardrails.json.
  • Overlay can NEVER loosen: a disabled/absent platform field leaves the agent's setting untouched; there is no "allow-override".
  • Effective merge is audited with per-field layer attribution.
  • Unit tests for every ratchet rule incl. the no-loosen invariant.

Supersedes/links #238 (platform-authored denied_command_patterns is just the customRules/security.customPatterns slice of this broader overlay).


Part 2 — Remove the DB-mode guardrails construct (unused)

guardrails.json has a second, unused load path: DB mode via FORGE_GUARDRAILS_DB (a MongoDB URI). It loads the entire config from Mongo and is mutually exclusive with the file (DB wins, file ignored). It was never adopted, adds a heavy dependency, and is the wrong model for "further restrict" (replacement, not merge) — the Part 1 overlay is the correct mechanism for platform control.

Remove

  • forge-cli/runtime/guardrails_engine.go: NewDBGuardrailEngine, the useDB field + its branch (~line 148), and the go.mongodb.org/mongo-driver/mongo + .../options imports.
  • forge-cli/runtime/guardrails_loader.go: the DB-mode branch (~lines 112–158), EnvGuardrailsDB / EnvGuardrailsDBRequired, dbModeRequired(), and the file/DB exclusivity warning (dbFileExclusivityOnce, guardrailsFilePathIfPresent if only used for that warning).
  • forge-cli/cmd/guardrails.go: DB-mode help text.
  • Tests referencing DB mode in guardrails_loader_test.go / guardrails_test.go.
  • Run go mod tidydrops the go.mongodb.org/mongo-driver dependency from forge-cli entirely (confirmed: it's used ONLY by the guardrails DB path). Reduces dependency + vuln surface.
  • Docs: docs/security/guardrails.md (remove the DB-vs-file section).

Acceptance

  • File mode is the only agent-level guardrails source; platform control is via the Part 1 overlay.
  • go.mongodb.org/mongo-driver no longer in forge-cli's go.mod.
  • No dangling references to FORGE_GUARDRAILS_DB*; docs updated.

Suggested order

Part 1 (overlay) first so platform control exists, then Part 2 (remove DB mode) so there's no window without a platform lever. Related: #238, platform_policy_layers.go / platform_policy_enforce.go (the pattern to mirror).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestforge-cliAffects the forge-cli command-line tool (init, run, build, mcp commands)forge-coreAffects the forge-core library (runtime, security, types, llm, mcp, auth)securitySecurity vulnerability fixes

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions