Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ secops-approve-prs:
secops-test-python:
uv run --with pytest pytest agents/secops/tests/test_skills.py

secops-skill-scan:
rm -rf .tmp/skill-scanner/secops
uv run --no-project --python .venv/bin/python tools/prepare_skill_scan.py --source agents/secops --dest .tmp/skill-scanner/secops
uvx --from cisco-ai-skill-scanner skill-scanner scan .tmp/skill-scanner/secops --format summary --format markdown --detailed --output-markdown .tmp/skill-scanner/secops-scan.md

secops-a2a:
uv run --no-project --python .venv/bin/python agents/secops/a2a_server.py

Expand Down
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ SHADI is designed for environments where agents are long-lived, hold real creden
- SQLCipher-backed encrypted local memory (`shadi_memory`).
- Python bindings (`shadi_py`) for secrets, memory, and sandboxed execution.
- SLIM transport integration for secure agent-to-agent messaging.
- A practical SecOps agent demo and launch scripts.
- Example agents and demos, including a practical SecOps workflow and launch scripts.

Recent updates in this branch of the project:

- SecOps now treats container CVEs as rebuild or base-image refresh work instead of mutating Dockerfiles with ad-hoc package upgrade lines.
- Dockerfile resolution for container findings is workflow-first, using `.github/workflows/*` metadata before repo-wide scanning.
- Demo launchers are hardened for the optional 1Password backend by reading required secrets before entering the sandbox.
- Avatar surfaces clearer SLIM handshake errors when the SecOps A2A side is unavailable or misconfigured.

## Repository layout

Expand All @@ -22,7 +29,7 @@ SHADI is designed for environments where agents are long-lived, hold real creden
- `crates/shadi_memory`: SQLCipher memory library and CLI (`shadi-memory`).
- `crates/shadi_py`: Python extension module `shadi`.
- `crates/agent_transport_slim` + `crates/slim_mas`: secure transport and moderation helpers.
- `agents/secops`: SecOps agent, A2A server, and skill implementation.
- `agents/secops`: example SecOps workload, A2A server, and skill implementation.
- `docs`: architecture, security, CLI, demos, and integration docs.
- `scripts`: local launch helpers for SLIM + agent demos.

Expand Down Expand Up @@ -141,7 +148,7 @@ The module exposes bindings for:

## SecOps demo and launch scripts

The repo includes runnable examples under `agents/secops` and helper scripts in `scripts/`.
The repo includes runnable demo workloads under `agents/secops` and helper scripts in `scripts/`.

Common local flow:

Expand All @@ -152,6 +159,18 @@ Common local flow:
./scripts/launch_avatar.sh
```

Focused Python tests for the SecOps skill:

```bash
just secops-test-python
```

Security scan for the SecOps skill package:

```bash
just secops-skill-scan
```

See `scripts/README.md` and `docs/demo.md` for full setup details.

## Documentation
Expand All @@ -164,6 +183,7 @@ Primary docs live in `docs/`:
- `docs/cli.md`: complete CLI reference
- `docs/sandbox.md`: policy model and profile behavior
- `docs/demo.md`: demo walkthrough
- `docs/secops_agent.md`: SecOps demo workload and remediation behavior

Build/serve docs locally:

Expand Down
32 changes: 16 additions & 16 deletions docs/api_integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ To list secrets stored for an app or agent, use `shadictl`:

```bash
cargo run -p shadictl -- --list-keychain --list-prefix agents/
cargo run -p shadictl -- --list-keychain --list-prefix secops/
cargo run -p shadictl -- --list-keychain --list-prefix apps/
```

Avoid printing secret values. Pass key names to helpers that resolve secrets
Expand All @@ -45,14 +45,14 @@ To list or search long-term memory in the encrypted SQLCipher store, use the
cargo run -p shadictl -- -- memory list \
--db "${SHADI_TMP_DIR:-./.tmp}/shadi-memory.db" \
--key-name shadi/memory/sqlcipher_key \
--scope secops --limit 50
--scope app --limit 50
```

```bash
cargo run -p shadictl -- -- memory search \
--db "${SHADI_TMP_DIR:-./.tmp}/shadi-memory.db" \
--key-name shadi/memory/sqlcipher_key \
--scope secops --query dependabot --limit 10
--scope app --query policy --limit 10
```

## Integration overview
Expand Down Expand Up @@ -103,8 +103,8 @@ fn main() -> Result<(), SecretError> {
let mut session = SessionContext::new("agent-1", "session-1");
session.verified = true;

access.put_for_session(&session, "secops/token", b"secret", SecretPolicy::default())?;
let secret = access.get_for_session(&session, "secops/token")?;
access.put_for_session(&session, "app/config", b"secret", SecretPolicy::default())?;
let secret = access.get_for_session(&session, "app/config")?;
let value = secret.expose(|bytes| bytes.to_vec());

println!("secret len: {}", value.len());
Expand Down Expand Up @@ -157,9 +157,9 @@ store.set_verifier(lambda agent_id, session_id, presentation, claims: True)
session = PySessionContext("agent-1", "session-1")
store.verify_session(session, b"didvc-presentation")

store.put(session, "secops/token", b"secret")
print(store.get(session, "secops/token"))
store.delete(session, "secops/token")
store.put(session, "app/config", b"secret")
print(store.get(session, "app/config"))
store.delete(session, "app/config")
```

### Example: integrate with an app session
Expand Down Expand Up @@ -196,12 +196,12 @@ store.put(session, "app/config", b"value")
from shadi import SqlCipherMemoryStore

store = SqlCipherMemoryStore(
db_path="./.tmp/shadi-secops/secops_memory.db",
key_name="secops/memory_key",
db_path="./.tmp/shadi-app/app_memory.db",
key_name="app/memory_key",
)

store.put("secops", "security_report", "{\"status\":\"ok\"}")
latest = store.get_latest("secops", "security_report")
store.put("app", "latest_state", "{\"status\":\"ok\"}")
latest = store.get_latest("app", "latest_state")
print(latest.payload if latest else "no entry")
```

Expand Down Expand Up @@ -286,7 +286,7 @@ access to the agent sessions.
## Secret store naming in Rust vs Python

Rust and Python use the same underlying SHADI secret store. The "name" you see
in examples is just the secret key string (for example, `secops/token` or
in examples is just the secret key string (for example, `app/config` or
`agents/agent-a/did`). There is no separate store per language.

If you see different names between Rust and Python examples, it is only a
Expand All @@ -313,8 +313,8 @@ For Python agents, you can run under the sandbox using the JSON policy runner:

```bash
./.venv/bin/python tools/run_sandboxed_agent.py \
--policy policies/demo/secops-a.json \
-- ./.venv/bin/python agents/secops/a2a_server.py
--policy ./sandbox.json \
-- ./.venv/bin/python ./your_agent.py
```

`net_allow` in the policy file is enforced by the Python runner using a
Expand All @@ -325,7 +325,7 @@ sandbox and inject it as an environment variable:

```bash
cargo run -p shadictl -- \
--inject-keychain secops/token=GITHUB_TOKEN \
--inject-keychain app/config=APP_CONFIG \
-- \
./your-agent
```
Expand Down
92 changes: 82 additions & 10 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@ actions and prevent unauthorized data access or exfiltration.
### 2) Sandbox layer
- **macOS**: Seatbelt profile enforcement for filesystem and network policies.
- **Windows**: AppContainer + ACL allowlists + Job Objects (kill-on-close).
- **CLI**: `shadi` provides command blocklists and a JSON policy format.
- **CLI**: `shadi` provides JSON policy loading, profile defaults, optional command blocklists, and brokered secret injection.
- **Portable launcher model**: `shadi` supports built-in profiles
(`strict`, `balanced`, `connected`) for portable secure launch defaults.
- **Launch-time enforcement**: Policy is resolved before the agent process starts, so the sandbox is not a prompt-level suggestion that the agent can rewrite from inside the session.
- **Operational hardening**: macOS launcher support now resolves relative paths before emitting Seatbelt rules and accounts for required local IPC paths such as 1Password and SLIM runtime state.

#### Key modules
- `crates/shadi_sandbox/src/policy.rs`: policy model and helpers.
Expand All @@ -68,14 +70,14 @@ actions and prevent unauthorized data access or exfiltration.
### 3) Memory layer
- **Local encrypted store**: SQLCipher-backed SQLite for portable, on-device memory.
- **Key management**: Encryption keys live in SHADI secrets (keychain backed).
- **Agent usage**: SecOps writes summaries to the encrypted store; ADK memory remains in-process unless configured for persistent backends.
- **Agent usage**: workloads running on SHADI can persist local state in the encrypted store; the SecOps demo writes summaries there, while ADK memory remains in-process unless configured for persistent backends.

#### Key modules
- `crates/shadi_memory/src/lib.rs`: SQLCipher store and query helpers.
- `crates/shadi_memory/src/main.rs`: shadi-memory CLI.
- `crates/shadictl/src/main.rs`: `shadictl memory` helper.
- `crates/shadi_py/src/lib.rs`: SQLCipher bindings.
- `agents/secops/skills.py`: SecOps summary persistence.
- `agents/secops/skills.py`: example summary persistence used by the SecOps demo.

### 4) Transport layer
- **SLIM/A2A**: MLS provides confidentiality and integrity between agents.
Expand All @@ -87,6 +89,9 @@ actions and prevent unauthorized data access or exfiltration.
### 5) Brokered secret injection (optional)
- If sandbox rules prevent keystore access, secrets can be brokered outside the
sandbox and injected as environment variables into the agent process.
- This is also the fallback path used by the demo launchers when the optional
1Password backend is enabled: required items are read in the foreground and
exported into the sandboxed process environment.

#### Key modules
- `crates/shadictl/src/main.rs`: `--inject-keychain` and policy enforcement.
Expand Down Expand Up @@ -114,9 +119,10 @@ The CLI combines profile defaults, policy file settings, and explicit flags:
- CLI flags override or extend resulting policy.
- The effective policy can be printed with `--print-policy`.

## SecOps agent architecture
The SecOps agent runs locally under SHADI and uses the Python bindings for secrets
plus GitHub APIs for security signals.
## Demo workload: SecOps agent
The SecOps agent is an example workload that runs on top of SHADI. It uses the
Python bindings for secrets plus GitHub APIs for security signals, but it is
not part of the core runtime itself.

#### Key modules
- `agents/secops/skills.py`: skills to collect alerts and issues.
Expand All @@ -128,7 +134,71 @@ plus GitHub APIs for security signals.
1. Read config from secops.toml.
2. Fetch GitHub token and workspace path from SHADI.
3. Collect Dependabot alerts and security-labeled issues.
4. Write `secops_security_report.json` to the workspace.
4. Collect code-scanning alerts for container findings via GitHub code scanning.
5. For dependency alerts, patch supported manifests and stage repo-relative changes.
6. For container CVEs, locate the authoritative Dockerfile from GitHub workflow metadata when possible and recommend image rebuilds or base-image refreshes instead of ad-hoc package-install edits.
7. Create remediation issues and optional PRs, then write `secops_security_report.json` to the workspace.

## Updated system view

```mermaid
flowchart TB
subgraph Operator[Operator and Control Plane]
Human[Human operator]
Config[secops.toml and policy JSON]
Launchers[Launch scripts and shadictl]
end

subgraph Trust[Identity and Secret Plane]
Verify[PySessionContext and verifier]
Secrets[ShadiStore]
Keychain[OS keychain or 1Password backend]
MemoryKey[SQLCipher memory key]
end

subgraph Runtime[Sandboxed Runtime Plane]
Sandbox[shadi_sandbox policy enforcement]
Avatar[Avatar ADK agent]
SecOps[SecOps agent or A2A server]
Memory[SqlCipherMemoryStore]
end

subgraph External[External Services]
GitHub[GitHub APIs and gh CLI]
Models[LLM provider endpoints]
SLIM[SLIM or A2A transport]
end

Human --> Launchers
Config --> Launchers
Launchers --> Sandbox
Human --> Verify
Verify --> Secrets
Secrets --> Keychain
Secrets --> MemoryKey
Sandbox --> Avatar
Sandbox --> SecOps
Avatar --> SLIM
SecOps --> SLIM
SecOps --> GitHub
SecOps --> Models
SecOps --> Memory
MemoryKey --> Memory
Avatar -. verified secret reads .-> Secrets
SecOps -. verified secret reads .-> Secrets
```

The main architecture update is that SHADI now has a clearer split between:
- control-plane launch logic that resolves policy and optional secret brokerage before process start,
- runtime enforcement that the agent cannot weaken by rewriting a local denylist path string,
- and application-layer behavior implemented by example workloads such as SecOps remediation planning and Avatar-to-SecOps orchestration.

## Demo workload behavior: SecOps remediation model

- Dependency remediation still edits supported manifests directly and can open PRs.
- Container CVEs are handled as rebuild guidance, not by mutating Dockerfiles with ad-hoc OS package commands.
- Dockerfile discovery prefers `.github/workflows/*` as the authoritative source of build definitions, then falls back to portable filesystem scanning.
- If only guidance is needed, SecOps opens a remediation issue so the repo owner can refresh the base image or rebuild the container in the right place.

## Data flow (high level)
1. Human identity material is ingested (OpenPGP or seed bytes).
Expand Down Expand Up @@ -210,8 +280,9 @@ flowchart LR
- **Stopped**: Message tampering; MLS provides integrity/authentication.

### Privilege escalation
- **Stopped**: Running blocked commands via CLI blocklist.
- **Mitigated**: Kernel-level constraints remain even if the agent tries to evade.
- **Mitigated**: Prompt-level or path-level agent reasoning by applying sandbox policy before launch.
- **Mitigated**: Running blocked commands via CLI blocklist when that feature is used.
- **Mitigated**: Kernel-level constraints remain even if the agent tries to evade application-layer logic.

## Threat-to-control mapping

Expand All @@ -221,7 +292,7 @@ flowchart LR
| Secret theft at rest | OS keystore storage | Blocked |
| Secret exfiltration in sandbox | OS sandbox + net block | Blocked |
| Unauthorized file access | Seatbelt/AppContainer allowlists | Blocked |
| Destructive commands | CLI blocklist | Blocked |
| Destructive commands | CLI blocklist | Mitigated |
| Message interception | MLS in SLIM | Blocked |
| Message tampering | MLS integrity | Blocked |
| Agent identity substitution | HKDF derivation + verify-agent-identity | Blocked (with verification) |
Expand All @@ -231,6 +302,7 @@ flowchart LR
- Host OS compromise or kernel-level malware can bypass sandbox controls.
- Metadata leakage (timing, sizes, endpoints) is not fully addressed in v1.
- ACL changes on Windows could be interrupted before rollback in a crash.
- Application-level path deny rules are weaker than OS-enforced sandbox restrictions; do not rely on path matching alone for high-assurance policy.

## Deployment guidance
- Prefer running agents under the sandbox with a JSON policy file.
Expand Down
3 changes: 3 additions & 0 deletions docs/assets/img/logo-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/assets/img/logo-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading