- Updated all 150+ :tangle headers across harness/ and skills/ to use elisp (expand-file-name) to target INSTALL_DIR dynamically. - Cleaned up environment/ directory depth by moving memory-image.lisp to state/. - Moved test scripts to tests/ and deleted redundant chat scripts.
14 KiB
SKILL: Deterministic Engine Bouncer (Authorization Gate)
- Overview
- Package Context
- Security Vectors
- Runtime Guard
- Flight Plan Workflow
- Skill Gate
- Quick Reference
- See Also
Overview
The Bouncer Skill is the physical security layer of OpenCortex. While the Policy skill enforces constitutional invariants (transparency, autonomy, modularity), the Bouncer enforces operational security checks.
Think of Policy as the constitution and Bouncer as the bouncer at the door:
- Policy asks: "Is this action aligned with our values?"
- Bouncer asks: "Is this action safe to execute?"
The Flight Plan Pattern
High-risk actions don't simply pass or fail—they can enter the "Flight Plan" approval workflow:
- Bouncer intercepts a risky action
- Creates an Org node ("Flight Plan") describing the action
- User manually approves the flight plan in Emacs
- Bouncer detects approval on next heartbeat
- Action is re-injected with `approved = t` flag, bypassing the gate
This creates human-in-the-loop oversight for dangerous operations without blocking the system entirely.
Why a Separate Skill?**
Security and policy are separated for clarity and auditability:
- Policy decisions can be explained (they reference invariants)
- Bouncer decisions are technical (they reference threat vectors)
When something is blocked, the logs clearly show which layer blocked it and why.
Package Context
(in-package :opencortex)
Security Vectors
The Bouncer implements the 5-Vector security model:
| Vector | Threat | Response |
|---|---|---|
| Secret Exposure | API keys, passwords in output | Hard block |
| Network Exfiltration | Data sent to unauthorized hosts | Approval required |
| Shell Execution | Arbitrary command execution | Approval required |
| File Modification | Writing/deleting files | Soft check |
| Eval Execution | Arbitrary code evaluation | Approval required |
Secret Exposure Detection
The vault stores sensitive credentials. This check scans action text for vault secrets to prevent accidental exposure.
(defun bouncer-scan-secrets (text)
"Scans TEXT for known secrets from the vault.
RETURNS: The name of the matched secret, or NIL if text is clean.
This prevents the catastrophic failure mode where the agent
accidentally echoes an API key in its response or log output.
The check uses substring matching (not regex) for reliability.
Only secrets longer than 5 characters are checked to avoid
false positives on common words."
(when (and text (stringp text))
(let ((found-secret nil))
(maphash (lambda (key val)
;; Only check secrets of meaningful length
(when (and val (stringp val) (> (length val) 5))
;; Search for secret value in action text
(when (search val text)
(setf found-secret key))))
opencortex::*vault-memory*)
found-secret)))
Network Exfiltration Detection
Detects when shell commands try to send data to untrusted network destinations.
(defvar *bouncer-network-whitelist*
'("api.telegram.org" "matrix.org" "googleapis.com" "openai.com" "anthropic.com")
"Domains that the Bouncer considers safe for outbound connections.
This whitelist should be minimal—only services explicitly configured
as gateways. All other outbound connections require approval.")
(defun bouncer-check-network-exfil (cmd)
"Detects if CMD attempts to contact an unwhitelisted external host.
Returns T if the command targets an unknown external host.
Returns NIL if the command is clean or only contacts whitelisted hosts.
The check looks for HTTP/HTTPS/FTP URLs and extracts the domain.
If the domain isn't in *bouncer-network-whitelist*, it's flagged."
(when (and cmd (stringp cmd))
;; Look for URL patterns in the command
(when (cl-ppcre:scan "(http|https|ftp)://([\\w\\.-]+)" cmd)
(multiple-value-bind (match regs)
(cl-ppcre:scan-to-strings "(http|https|ftp)://([\\w\\.-]+)" cmd)
(declare (ignore match))
(let ((domain (aref regs 1)))
;; Check if domain is whitelisted
(not (some (lambda (safe) (search safe domain))
*bouncer-network-whitelist*)))))))
Runtime Guard
bouncer-check: Main Security Gate
(defun bouncer-check (action context)
"The 5-Vector security gate for high-risk actions.
Evaluates an action against all security vectors and either:
- Returns the action unchanged (pass)
- Returns a blocking LOG event (hard block)
- Returns an approval-required EVENT (soft block)
Vector evaluation order:
1. Already approved actions pass immediately
2. Secret exposure → hard block
3. Network exfiltration → approval required
4. High-impact targets → approval required
The context parameter is not used directly but provided for
consistency with the skill gate signature."
(declare (ignore context))
(let* ((target (getf action :target))
(payload (getf action :payload))
(text (or (getf payload :text) (getf action :text)))
;; Extract cmd from direct shell or tool-mediated shell call
(cmd (or (getf payload :cmd)
(when (and (eq target :tool)
(equal (getf payload :tool) "shell"))
(getf (getf payload :args) :cmd))))
(approved (getf action :approved)))
(cond
;; Vector 0: Already approved actions pass through
(approved
action)
;; Vector 1: Secret Exposure (Hard Block)
;; If any vault secret is found in the action text, block immediately
((and text (bouncer-scan-secrets text))
(let ((secret-name (bouncer-scan-secrets text)))
(harness-log "SECURITY VIOLATION: Blocked potential leak of secret '~a'" secret-name)
(list :type :LOG
:payload (list :level :error
:text (format nil "Action blocked: Potential exposure of '~a'" secret-name)))))
;; Vector 2: Network Exfiltration (Soft Block)
;; Shell commands targeting unknown hosts require approval
((and (or (eq target :shell)
(and (eq target :tool)
(equal (getf payload :tool) "shell")))
(bouncer-check-network-exfil cmd))
(harness-log "SECURITY WARNING: External network call detected. Queuing for approval.")
(list :type :EVENT
:payload (list :sensor :approval-required
:action action)))
;; Vector 3: High-Impact Targets (Soft Block)
;; Shell execution, file repair, and eval require approval
((or (member target '(:shell))
(and (eq target :tool)
(member (getf payload :tool) '("shell" "repair-file") :test #'string=))
(and (eq target :emacs)
(eq (getf payload :action) :eval)))
(harness-log "SECURITY: High-impact action requires approval: ~a"
(or (getf payload :tool) target))
(list :type :EVENT
:payload (list :sensor :approval-required
:action action)))
;; Vector 4: Default pass
(t
action))))
Flight Plan Workflow
Processing Approvals
When a flight plan is approved in Emacs, the Bouncer detects it and re-injects the action.
(defun bouncer-process-approvals ()
"Scans the object store for APPROVED flight plans and re-injects them.
This function is called on every heartbeat, allowing the agent to
check for approvals without blocking the main signal pipeline.
Flight Plan format:
- Has TAGS including \"FLIGHT_PLAN\"
- Has TODO set to \"APPROVED\"
- Has ACTION containing the serialized action plist
When an approved flight plan is found:
1. Deserialize the action from the ACTION attribute
2. Mark the action as :approved = t (bypasses security gate)
3. Re-inject into the signal pipeline
4. Mark the flight plan as DONE
Returns T if any flight plans were processed."
(let ((approved-nodes (list-objects-with-attribute :TODO "APPROVED"))
(found-any nil))
(dolist (node approved-nodes)
(let* ((tags (getf (org-object-attributes node) :TAGS))
(action-str (getf (org-object-attributes node) :ACTION)))
;; Only process flight plans (not other APPROVED items)
(when (and (member "FLIGHT_PLAN" tags :test #'string-equal)
action-str)
(harness-log "BOUNCER: Found approved flight plan '~a'. Re-injecting..."
(org-object-id node))
(let ((action (ignore-errors (read-from-string action-str))))
(when action
;; Mark as approved to bypass the security gate on re-injection
(setf (getf action :approved) t)
;; Re-inject the action into the signal pipeline
(inject-stimulus action)
;; Mark the flight plan as done
(setf (getf (org-object-attributes node) :TODO) "DONE")
(setq found-any t))))))
found-any))
Creating Flight Plans
When the Bouncer intercepts a high-risk action, it creates a flight plan node for manual approval.
(defun bouncer-create-flight-plan (blocked-action)
"Creates an Org node representing a pending flight plan for manual approval.
BLOCKED-ACTION is the action plist that was intercepted.
The flight plan node contains:
- A title describing the action
- TODO set to PLAN (awaiting approval)
- TAGS including FLIGHT_PLAN
- ACTION attribute containing the serialized action
The user reviews the flight plan and changes TODO to APPROVED.
On the next heartbeat, bouncer-process-approvals will detect
the approval and re-inject the action.
Returns the generated org-id for the flight plan."
(let ((id (org-id-new)))
(harness-log "BOUNCER: Creating flight plan node '~a'..." id)
;; Inject a node creation request
(list :type :REQUEST
:target :emacs
:payload (list :action :insert-node
:id id
:attributes (list
:TITLE "Flight Plan: High-Risk Action"
:TODO "PLAN"
:TAGS '("FLIGHT_PLAN")
:ACTION (format nil "~s" blocked-action)))))
Skill Gate
Main Gate Function
(defun bouncer-deterministic-gate (action context)
"Main deterministic gate for the Bouncer skill.
Handles three types of signals:
1. :approval-required - Create a flight plan for the blocked action
2. :heartbeat - Process any pending approvals
3. otherwise - Run security check on the action
The trigger is always true (bouncer evaluates all actions)
because security cannot be selective."
(let* ((payload (getf context :payload))
(sensor (getf payload :sensor)))
(case sensor
;; Signal type 1: Action was blocked, create flight plan
(:approval-required
(let* ((blocked-action (getf payload :action)))
(bouncer-create-flight-plan blocked-action)))
;; Signal type 2: Heartbeat, check for approvals
(:heartbeat
(bouncer-process-approvals)
;; After processing approvals, still run the security check
(if action
(bouncer-check action context)
action))
;; Signal type 3: Normal action, run security check
(otherwise
(if action
(bouncer-check action context)
action)))))
Skill Registration
(defskill :skill-bouncer
:priority 150
:trigger (lambda (ctx) (declare (ignore ctx)) t)
:probabilistic nil
:deterministic #'bouncer-deterministic-gate)
Quick Reference
Security Vectors Summary
| Vector | Check | Response |
|---|---|---|
| Secret Exposure | `bouncer-scan-secrets` | Hard block |
| Network Exfil | `bouncer-check-network-exfil` | Approval required |
| Shell Execution | target = :shell or tool = "shell" | Approval required |
| Eval Execution | target = :emacs and action = :eval | Approval required |
| File Repair | tool = "repair-file" | Approval required |
Flight Plan Lifecycle
- High-risk action intercepted → `:approval-required` signal
- Flight plan node created in Emacs with `TODO: PLAN`
- User reviews and sets `TODO: APPROVED`
- Next heartbeat detects approval
- Action re-injected with `approved = t`
- Security gate bypassed, action executes
- Flight plan marked `TODO: DONE`
See Also
- Credentials Vault - Where secrets are stored
- Policy Skill - Constitutional constraints
- Act Stage - Where gates are invoked