superuser 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,215 @@
1
+ Metadata-Version: 2.4
2
+ Name: superuser
3
+ Version: 0.1.0
4
+ Summary: A permission-gated superuser (sysadmin) MCP agent with Cisco AI Defense runtime guardrails.
5
+ Author: Super
6
+ License: Apache-2.0
7
+ Keywords: mcp,agent,sysadmin,superuser,cisco,ai-defense,guardrails
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: mcp<2,>=1.2.0
11
+ Requires-Dist: psutil>=5.9
12
+ Requires-Dist: PyYAML>=6.0
13
+ Requires-Dist: requests>=2.31
14
+ Provides-Extra: security
15
+ Requires-Dist: cisco-aidefense-sdk>=2.1.0; extra == "security"
16
+ Provides-Extra: billing
17
+ Requires-Dist: stripe>=11.0; extra == "billing"
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=8.0; extra == "dev"
20
+ Requires-Dist: ruff>=0.6; extra == "dev"
21
+ Provides-Extra: all
22
+ Requires-Dist: cisco-aidefense-sdk>=2.1.0; extra == "all"
23
+ Requires-Dist: stripe>=11.0; extra == "all"
24
+
25
+ # Super
26
+
27
+ <!-- mcp-name: io.github.fitzyracing1/superuser -->
28
+
29
+ A **permission-gated superuser agent** exposed over the [Model Context Protocol
30
+ (MCP)](https://modelcontextprotocol.io). It gives an AI agent real
31
+ system-administration powers on this machine — running shell commands, checking
32
+ system state, and acting on GitHub — while wrapping every action in guardrails:
33
+ a dangerous-command denylist, a human-approval gate for risky operations,
34
+ least-privilege GitHub access, an append-only audit log, and **Cisco AI Defense**
35
+ runtime inspection of every tool call and response.
36
+
37
+ The design goal is the version of a "superuser agent" you can actually ship:
38
+ powerful, but safe, auditable, and compatible with enterprise guardrails.
39
+
40
+ > **Landing site:** https://fitzyracing1.github.io/super-landing/ (source: [fitzyracing1/super-landing](https://github.com/fitzyracing1/super-landing)).
41
+ > The previous Pages site in this repo is preserved at tag `site-v1` / branch `site-archive`.
42
+
43
+ ## Why it's built this way
44
+
45
+ An agent that can do *anything* as root is a liability, not a product. Buyers
46
+ (and marketplace security reviews) expect scoping, approvals, and an audit
47
+ trail. So capabilities here always pass through a single choke point — the
48
+ `Guard` — which enforces, in order:
49
+
50
+ 1. **Cisco AI Defense** inspects the tool call (can block).
51
+ 2. **Cisco Duo Agentic Identity** authorizes the tool call per-policy (can block).
52
+ 3. **Approval gate** — anything at or above `approval_required_at` (default
53
+ `MEDIUM`) needs an explicit `confirm=true` from a human.
54
+ 4. The operation runs.
55
+ 5. **Cisco AI Defense** inspects the response before it is returned (catches
56
+ data leakage).
57
+ 6. **Stripe** meters the successful call (best-effort; never blocks).
58
+ 7. The outcome is written to the **audit log** as
59
+ `human owner -> agent -> tool -> action -> outcome`.
60
+
61
+ This mirrors the enterprise model: your app owns scoping + approvals + local
62
+ audit; **Cisco AI Defense** provides runtime threat inspection at the MCP layer;
63
+ **Cisco Duo Agentic Identity** provides per-tool-call authorization tied to an
64
+ accountable human owner; and **Stripe** turns metered tool calls into revenue.
65
+
66
+ ### Cisco Duo Agentic Identity (authorization)
67
+
68
+ Duo follows a "Duo decides, the gateway enforces" model. This agent registers as
69
+ a non-human identity, authenticates via the OAuth 2.1 **client-credentials**
70
+ grant (with an RFC 8707 resource indicator), and authorizes every tool call
71
+ either by matching the tool's required **scope** against what Duo granted, or by
72
+ calling an explicit Duo **decision endpoint**. Set `DUO_ENABLED=true` plus
73
+ `DUO_TOKEN_URL` / `DUO_CLIENT_ID` / `DUO_CLIENT_SECRET` (see `.env.example`). When
74
+ Duo is not configured, calls are allowed but flagged `governed: false`; in
75
+ `enforce` mode authorization errors fail closed.
76
+
77
+ ### Stripe usage-based billing (monetization)
78
+
79
+ Each authorized, successful tool call emits a Stripe **meter event**
80
+ (v2 billing meter events API), so the agent can be sold per-action. Configure a
81
+ meter in Stripe, then set `STRIPE_ENABLED=true`, `STRIPE_API_KEY`,
82
+ `STRIPE_CUSTOMER_ID`, and `STRIPE_METER_EVENT_NAME`. Per-tool weights let
83
+ higher-risk actions cost more. Billing is best-effort: metering failures are
84
+ logged and never block or fail the tool call (results are annotated
85
+ `billed: true|false`).
86
+
87
+ ## Tools
88
+
89
+ | Tool | Risk | Notes |
90
+ |------|------|-------|
91
+ | `system_status` | LOW | Host, CPU, memory, uptime |
92
+ | `disk_usage` | LOW | Per-partition disk usage |
93
+ | `list_processes` | LOW | Top processes by memory/cpu |
94
+ | `run_command` | HIGH* | Shell command; denylist + approval + audit |
95
+ | `github_get_repo` | LOW | Repo metadata (allowlisted repos only) |
96
+ | `github_list_issues` | LOW | Issues for an allowlisted repo |
97
+ | `github_create_issue_comment` | HIGH | Write; needs approval + `GITHUB_ALLOW_WRITES` |
98
+ | `security_status` | — | Reports the active guardrail configuration |
99
+
100
+ \* `run_command` is LOW risk only when its base command is in
101
+ `command_allowlist`; otherwise HIGH (requires `confirm=true`).
102
+
103
+ ## Install
104
+
105
+ Not yet on PyPI. Install straight from git:
106
+
107
+ ```bash
108
+ pip install "superuser @ git+https://github.com/fitzyracing1/Super.git"
109
+ pip install "superuser[all] @ git+https://github.com/fitzyracing1/Super.git" # + Cisco AI Defense + Stripe SDKs
110
+ ```
111
+
112
+ From a clone:
113
+
114
+ Requires Python 3.10+.
115
+
116
+ ```bash
117
+ pip install -e . # core agent (Duo authz uses requests; no extra dep)
118
+ pip install -e '.[security]' # + Cisco AI Defense SDK (cisco-aidefense-sdk)
119
+ pip install -e '.[billing]' # + Stripe SDK (usage-based billing)
120
+ pip install -e '.[all]' # security + billing
121
+ pip install -e '.[dev]' # + pytest / ruff
122
+ ```
123
+
124
+ ## Configure
125
+
126
+ Copy `.env.example` to `.env` (or use a YAML file — see `superuser.example.yaml`)
127
+ and set values. Key settings:
128
+
129
+ - `SUPERUSER_HUMAN_OWNER` — the human accountable for the agent's actions.
130
+ - `SUPERUSER_APPROVAL_AT` — risk threshold for the approval gate (`LOW`…`CRITICAL`).
131
+ - `AI_DEFENSE_*` — Cisco AI Defense credentials/mode (see below).
132
+ - `DUO_*` — Cisco Duo Agentic Identity authorization (see below).
133
+ - `STRIPE_*` — Stripe usage-based billing (see below).
134
+ - `GITHUB_TOKEN`, `GITHUB_ALLOWED_REPOS`, `GITHUB_ALLOW_WRITES` — least-privilege GitHub.
135
+
136
+ > For a full console-value → env-var mapping for all three services, see
137
+ > [`docs/INTEGRATIONS.md`](docs/INTEGRATIONS.md).
138
+
139
+ ### Cisco AI Defense
140
+
141
+ Install the SDK (`pip install 'superuser[security]'`) and provide credentials:
142
+
143
+ - **Explicit inspection** of this server's own tool calls/responses:
144
+ set `AI_DEFENSE_INSPECTION_API_KEY`.
145
+ - **Runtime protection** (`agentsec.protect()` auto-patches LLM + MCP clients):
146
+ set `AI_DEFENSE_API_MODE_LLM_ENDPOINT` + `AI_DEFENSE_API_MODE_LLM_API_KEY`,
147
+ or point `AI_DEFENSE_AGENTSEC_CONFIG` at an `agentsec.yaml`
148
+ (see `agentsec.example.yaml`).
149
+ - `AI_DEFENSE_MODE`: `enforce` (block), `monitor` (log only), or `off`.
150
+
151
+ If the SDK is not installed or no credentials are set, the agent still runs but
152
+ every result is annotated `protected: false` / `UNPROTECTED` — the gap is never
153
+ silent. In `enforce` mode, inspection errors **fail closed** (the call is
154
+ blocked); in `monitor` mode they fail open (logged, allowed).
155
+
156
+ ## Environment variables
157
+
158
+ | Area | Variables |
159
+ |------|-----------|
160
+ | Core | `SUPERUSER_CONFIG`, `SUPERUSER_HUMAN_OWNER`, `SUPERUSER_APPROVAL_AT`, `SUPERUSER_COMMAND_TIMEOUT`, `SUPERUSER_AUDIT_LOG` |
161
+ | Cisco AI Defense | `AI_DEFENSE_ENABLED` (default `true`; set `false` for local dev), `AI_DEFENSE_MODE`, `AI_DEFENSE_INSPECTION_API_KEY`, `AI_DEFENSE_API_MODE_LLM_ENDPOINT`, `AI_DEFENSE_API_MODE_LLM_API_KEY`, `AI_DEFENSE_AGENTSEC_CONFIG` |
162
+ | Cisco Duo | `DUO_ENABLED`, `DUO_MODE`, `DUO_AGENT_ID`, `DUO_TOKEN_URL`, `DUO_CLIENT_ID`, `DUO_CLIENT_SECRET`, `DUO_AUTHORIZATION_URL`, `DUO_RESOURCE`, `DUO_SCOPES` |
163
+ | Stripe | `STRIPE_ENABLED`, `STRIPE_API_KEY`, `STRIPE_CUSTOMER_ID`, `STRIPE_METER_EVENT_NAME`, `STRIPE_DEFAULT_VALUE` |
164
+ | GitHub | `GITHUB_TOKEN`, `GITHUB_ALLOWED_REPOS`, `GITHUB_ALLOW_WRITES`, `GITHUB_API_URL` |
165
+
166
+ With no keys set the server still runs: Duo and Stripe are off by default, and Cisco AI Defense runs unprotected (clearly flagged) if the SDK/keys are missing.
167
+
168
+ ## Run
169
+
170
+ ```bash
171
+ # Validate configuration and print guardrail/Cisco status, then exit:
172
+ superuser --check
173
+
174
+ # Start the MCP server (stdio transport, for MCP clients like Cursor/Claude):
175
+ superuser
176
+ # or: python -m superuser
177
+ ```
178
+
179
+ ### Register with an MCP client
180
+
181
+ Example client config (stdio):
182
+
183
+ ```json
184
+ {
185
+ "mcpServers": {
186
+ "superuser": {
187
+ "command": "superuser",
188
+ "env": {
189
+ "SUPERUSER_HUMAN_OWNER": "you@example.com",
190
+ "AI_DEFENSE_MODE": "enforce",
191
+ "AI_DEFENSE_INSPECTION_API_KEY": "..."
192
+ }
193
+ }
194
+ }
195
+ }
196
+ ```
197
+
198
+ ## Test
199
+
200
+ ```bash
201
+ pytest
202
+ ```
203
+
204
+ ## Security notes
205
+
206
+ - The dangerous-command denylist is a backstop, not the primary control. The
207
+ primary controls are Cisco AI Defense inspection and the approval gate.
208
+ - GitHub access **fails closed**: no token or an empty repo allowlist means no
209
+ access. Use a fine-grained, least-privilege token.
210
+ - Secrets are redacted in the audit log. Keep `.env` / `*.yaml` config files out
211
+ of version control (already in `.gitignore`).
212
+
213
+ ## License
214
+
215
+ Apache-2.0.
@@ -0,0 +1,191 @@
1
+ # Super
2
+
3
+ <!-- mcp-name: io.github.fitzyracing1/superuser -->
4
+
5
+ A **permission-gated superuser agent** exposed over the [Model Context Protocol
6
+ (MCP)](https://modelcontextprotocol.io). It gives an AI agent real
7
+ system-administration powers on this machine — running shell commands, checking
8
+ system state, and acting on GitHub — while wrapping every action in guardrails:
9
+ a dangerous-command denylist, a human-approval gate for risky operations,
10
+ least-privilege GitHub access, an append-only audit log, and **Cisco AI Defense**
11
+ runtime inspection of every tool call and response.
12
+
13
+ The design goal is the version of a "superuser agent" you can actually ship:
14
+ powerful, but safe, auditable, and compatible with enterprise guardrails.
15
+
16
+ > **Landing site:** https://fitzyracing1.github.io/super-landing/ (source: [fitzyracing1/super-landing](https://github.com/fitzyracing1/super-landing)).
17
+ > The previous Pages site in this repo is preserved at tag `site-v1` / branch `site-archive`.
18
+
19
+ ## Why it's built this way
20
+
21
+ An agent that can do *anything* as root is a liability, not a product. Buyers
22
+ (and marketplace security reviews) expect scoping, approvals, and an audit
23
+ trail. So capabilities here always pass through a single choke point — the
24
+ `Guard` — which enforces, in order:
25
+
26
+ 1. **Cisco AI Defense** inspects the tool call (can block).
27
+ 2. **Cisco Duo Agentic Identity** authorizes the tool call per-policy (can block).
28
+ 3. **Approval gate** — anything at or above `approval_required_at` (default
29
+ `MEDIUM`) needs an explicit `confirm=true` from a human.
30
+ 4. The operation runs.
31
+ 5. **Cisco AI Defense** inspects the response before it is returned (catches
32
+ data leakage).
33
+ 6. **Stripe** meters the successful call (best-effort; never blocks).
34
+ 7. The outcome is written to the **audit log** as
35
+ `human owner -> agent -> tool -> action -> outcome`.
36
+
37
+ This mirrors the enterprise model: your app owns scoping + approvals + local
38
+ audit; **Cisco AI Defense** provides runtime threat inspection at the MCP layer;
39
+ **Cisco Duo Agentic Identity** provides per-tool-call authorization tied to an
40
+ accountable human owner; and **Stripe** turns metered tool calls into revenue.
41
+
42
+ ### Cisco Duo Agentic Identity (authorization)
43
+
44
+ Duo follows a "Duo decides, the gateway enforces" model. This agent registers as
45
+ a non-human identity, authenticates via the OAuth 2.1 **client-credentials**
46
+ grant (with an RFC 8707 resource indicator), and authorizes every tool call
47
+ either by matching the tool's required **scope** against what Duo granted, or by
48
+ calling an explicit Duo **decision endpoint**. Set `DUO_ENABLED=true` plus
49
+ `DUO_TOKEN_URL` / `DUO_CLIENT_ID` / `DUO_CLIENT_SECRET` (see `.env.example`). When
50
+ Duo is not configured, calls are allowed but flagged `governed: false`; in
51
+ `enforce` mode authorization errors fail closed.
52
+
53
+ ### Stripe usage-based billing (monetization)
54
+
55
+ Each authorized, successful tool call emits a Stripe **meter event**
56
+ (v2 billing meter events API), so the agent can be sold per-action. Configure a
57
+ meter in Stripe, then set `STRIPE_ENABLED=true`, `STRIPE_API_KEY`,
58
+ `STRIPE_CUSTOMER_ID`, and `STRIPE_METER_EVENT_NAME`. Per-tool weights let
59
+ higher-risk actions cost more. Billing is best-effort: metering failures are
60
+ logged and never block or fail the tool call (results are annotated
61
+ `billed: true|false`).
62
+
63
+ ## Tools
64
+
65
+ | Tool | Risk | Notes |
66
+ |------|------|-------|
67
+ | `system_status` | LOW | Host, CPU, memory, uptime |
68
+ | `disk_usage` | LOW | Per-partition disk usage |
69
+ | `list_processes` | LOW | Top processes by memory/cpu |
70
+ | `run_command` | HIGH* | Shell command; denylist + approval + audit |
71
+ | `github_get_repo` | LOW | Repo metadata (allowlisted repos only) |
72
+ | `github_list_issues` | LOW | Issues for an allowlisted repo |
73
+ | `github_create_issue_comment` | HIGH | Write; needs approval + `GITHUB_ALLOW_WRITES` |
74
+ | `security_status` | — | Reports the active guardrail configuration |
75
+
76
+ \* `run_command` is LOW risk only when its base command is in
77
+ `command_allowlist`; otherwise HIGH (requires `confirm=true`).
78
+
79
+ ## Install
80
+
81
+ Not yet on PyPI. Install straight from git:
82
+
83
+ ```bash
84
+ pip install "superuser @ git+https://github.com/fitzyracing1/Super.git"
85
+ pip install "superuser[all] @ git+https://github.com/fitzyracing1/Super.git" # + Cisco AI Defense + Stripe SDKs
86
+ ```
87
+
88
+ From a clone:
89
+
90
+ Requires Python 3.10+.
91
+
92
+ ```bash
93
+ pip install -e . # core agent (Duo authz uses requests; no extra dep)
94
+ pip install -e '.[security]' # + Cisco AI Defense SDK (cisco-aidefense-sdk)
95
+ pip install -e '.[billing]' # + Stripe SDK (usage-based billing)
96
+ pip install -e '.[all]' # security + billing
97
+ pip install -e '.[dev]' # + pytest / ruff
98
+ ```
99
+
100
+ ## Configure
101
+
102
+ Copy `.env.example` to `.env` (or use a YAML file — see `superuser.example.yaml`)
103
+ and set values. Key settings:
104
+
105
+ - `SUPERUSER_HUMAN_OWNER` — the human accountable for the agent's actions.
106
+ - `SUPERUSER_APPROVAL_AT` — risk threshold for the approval gate (`LOW`…`CRITICAL`).
107
+ - `AI_DEFENSE_*` — Cisco AI Defense credentials/mode (see below).
108
+ - `DUO_*` — Cisco Duo Agentic Identity authorization (see below).
109
+ - `STRIPE_*` — Stripe usage-based billing (see below).
110
+ - `GITHUB_TOKEN`, `GITHUB_ALLOWED_REPOS`, `GITHUB_ALLOW_WRITES` — least-privilege GitHub.
111
+
112
+ > For a full console-value → env-var mapping for all three services, see
113
+ > [`docs/INTEGRATIONS.md`](docs/INTEGRATIONS.md).
114
+
115
+ ### Cisco AI Defense
116
+
117
+ Install the SDK (`pip install 'superuser[security]'`) and provide credentials:
118
+
119
+ - **Explicit inspection** of this server's own tool calls/responses:
120
+ set `AI_DEFENSE_INSPECTION_API_KEY`.
121
+ - **Runtime protection** (`agentsec.protect()` auto-patches LLM + MCP clients):
122
+ set `AI_DEFENSE_API_MODE_LLM_ENDPOINT` + `AI_DEFENSE_API_MODE_LLM_API_KEY`,
123
+ or point `AI_DEFENSE_AGENTSEC_CONFIG` at an `agentsec.yaml`
124
+ (see `agentsec.example.yaml`).
125
+ - `AI_DEFENSE_MODE`: `enforce` (block), `monitor` (log only), or `off`.
126
+
127
+ If the SDK is not installed or no credentials are set, the agent still runs but
128
+ every result is annotated `protected: false` / `UNPROTECTED` — the gap is never
129
+ silent. In `enforce` mode, inspection errors **fail closed** (the call is
130
+ blocked); in `monitor` mode they fail open (logged, allowed).
131
+
132
+ ## Environment variables
133
+
134
+ | Area | Variables |
135
+ |------|-----------|
136
+ | Core | `SUPERUSER_CONFIG`, `SUPERUSER_HUMAN_OWNER`, `SUPERUSER_APPROVAL_AT`, `SUPERUSER_COMMAND_TIMEOUT`, `SUPERUSER_AUDIT_LOG` |
137
+ | Cisco AI Defense | `AI_DEFENSE_ENABLED` (default `true`; set `false` for local dev), `AI_DEFENSE_MODE`, `AI_DEFENSE_INSPECTION_API_KEY`, `AI_DEFENSE_API_MODE_LLM_ENDPOINT`, `AI_DEFENSE_API_MODE_LLM_API_KEY`, `AI_DEFENSE_AGENTSEC_CONFIG` |
138
+ | Cisco Duo | `DUO_ENABLED`, `DUO_MODE`, `DUO_AGENT_ID`, `DUO_TOKEN_URL`, `DUO_CLIENT_ID`, `DUO_CLIENT_SECRET`, `DUO_AUTHORIZATION_URL`, `DUO_RESOURCE`, `DUO_SCOPES` |
139
+ | Stripe | `STRIPE_ENABLED`, `STRIPE_API_KEY`, `STRIPE_CUSTOMER_ID`, `STRIPE_METER_EVENT_NAME`, `STRIPE_DEFAULT_VALUE` |
140
+ | GitHub | `GITHUB_TOKEN`, `GITHUB_ALLOWED_REPOS`, `GITHUB_ALLOW_WRITES`, `GITHUB_API_URL` |
141
+
142
+ With no keys set the server still runs: Duo and Stripe are off by default, and Cisco AI Defense runs unprotected (clearly flagged) if the SDK/keys are missing.
143
+
144
+ ## Run
145
+
146
+ ```bash
147
+ # Validate configuration and print guardrail/Cisco status, then exit:
148
+ superuser --check
149
+
150
+ # Start the MCP server (stdio transport, for MCP clients like Cursor/Claude):
151
+ superuser
152
+ # or: python -m superuser
153
+ ```
154
+
155
+ ### Register with an MCP client
156
+
157
+ Example client config (stdio):
158
+
159
+ ```json
160
+ {
161
+ "mcpServers": {
162
+ "superuser": {
163
+ "command": "superuser",
164
+ "env": {
165
+ "SUPERUSER_HUMAN_OWNER": "you@example.com",
166
+ "AI_DEFENSE_MODE": "enforce",
167
+ "AI_DEFENSE_INSPECTION_API_KEY": "..."
168
+ }
169
+ }
170
+ }
171
+ }
172
+ ```
173
+
174
+ ## Test
175
+
176
+ ```bash
177
+ pytest
178
+ ```
179
+
180
+ ## Security notes
181
+
182
+ - The dangerous-command denylist is a backstop, not the primary control. The
183
+ primary controls are Cisco AI Defense inspection and the approval gate.
184
+ - GitHub access **fails closed**: no token or an empty repo allowlist means no
185
+ access. Use a fine-grained, least-privilege token.
186
+ - Secrets are redacted in the audit log. Keep `.env` / `*.yaml` config files out
187
+ of version control (already in `.gitignore`).
188
+
189
+ ## License
190
+
191
+ Apache-2.0.
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "superuser"
7
+ version = "0.1.0"
8
+ description = "A permission-gated superuser (sysadmin) MCP agent with Cisco AI Defense runtime guardrails."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "Apache-2.0" }
12
+ authors = [{ name = "Super" }]
13
+ keywords = ["mcp", "agent", "sysadmin", "superuser", "cisco", "ai-defense", "guardrails"]
14
+
15
+ dependencies = [
16
+ "mcp>=1.2.0,<2",
17
+ "psutil>=5.9",
18
+ "PyYAML>=6.0",
19
+ "requests>=2.31",
20
+ ]
21
+
22
+ [project.optional-dependencies]
23
+ # Cisco AI Defense runtime guardrails. Imported lazily so the server still
24
+ # runs (in an unprotected, clearly-warned state) when this is not installed.
25
+ security = ["cisco-aidefense-sdk>=2.1.0"]
26
+ # Stripe usage-based billing. Optional; metering degrades gracefully without it.
27
+ billing = ["stripe>=11.0"]
28
+ dev = ["pytest>=8.0", "ruff>=0.6"]
29
+ all = ["cisco-aidefense-sdk>=2.1.0", "stripe>=11.0"]
30
+
31
+ [project.scripts]
32
+ superuser = "superuser.__main__:main"
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["src"]
36
+
37
+ [tool.ruff]
38
+ line-length = 100
39
+ target-version = "py310"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """Superuser: a permission-gated sysadmin MCP agent with Cisco AI Defense guardrails."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ __all__ = ["__version__"]
@@ -0,0 +1,62 @@
1
+ """Entry point: `superuser` / `python -m superuser`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import logging
7
+ import sys
8
+
9
+ from .config import Config
10
+ from .server import build_server
11
+
12
+
13
+ def main(argv: list[str] | None = None) -> int:
14
+ parser = argparse.ArgumentParser(
15
+ prog="superuser",
16
+ description="Permission-gated superuser MCP agent with Cisco AI Defense guardrails.",
17
+ )
18
+ parser.add_argument("--config", help="Path to a YAML config file (or set SUPERUSER_CONFIG).")
19
+ parser.add_argument(
20
+ "--transport", default="stdio", choices=["stdio", "sse", "streamable-http"],
21
+ help="MCP transport (default: stdio).",
22
+ )
23
+ parser.add_argument("--log-level", default="INFO")
24
+ parser.add_argument(
25
+ "--check", action="store_true",
26
+ help="Validate config and print security status, then exit (no server).",
27
+ )
28
+ args = parser.parse_args(argv)
29
+
30
+ logging.basicConfig(
31
+ level=getattr(logging, args.log_level.upper(), logging.INFO),
32
+ format="%(asctime)s %(levelname)s %(name)s: %(message)s",
33
+ stream=sys.stderr,
34
+ )
35
+
36
+ config = Config.load(args.config)
37
+ server = build_server(config)
38
+
39
+ if args.check:
40
+ from .authz import DuoAuthorizer
41
+ from .billing import StripeMeter
42
+ from .security import CiscoAIDefense
43
+
44
+ defense = CiscoAIDefense(config.cisco)
45
+ authorizer = DuoAuthorizer(config.duo)
46
+ meter = StripeMeter(config.stripe)
47
+ print("Superuser agent configuration OK.")
48
+ print(f" Human owner: {config.human_owner}")
49
+ print(f" Approval required at: {config.approval_required_at.name}")
50
+ print(f" Audit log: {config.audit_log_path}")
51
+ print(f" Cisco AI Defense: {defense.status}")
52
+ print(f" Cisco Duo (authz): {authorizer.status}")
53
+ print(f" Stripe billing: {meter.status}")
54
+ print(f" GitHub configured: {bool(config.github.token)}")
55
+ return 0
56
+
57
+ server.run(transport=args.transport)
58
+ return 0
59
+
60
+
61
+ if __name__ == "__main__":
62
+ raise SystemExit(main())
@@ -0,0 +1,70 @@
1
+ """Append-only audit logging.
2
+
3
+ Every tool invocation produces an identity-correlated record:
4
+ ``human owner -> agent -> tool -> action -> outcome``. This is the local
5
+ counterpart to the audit chain enforced by Cisco Duo's MCP gateway, and it is
6
+ what makes the agent's actions reviewable after the fact.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import os
13
+ import threading
14
+ from datetime import datetime, timezone
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ # Argument keys whose values must never be written to the audit log verbatim.
19
+ _SENSITIVE_KEYS = {"token", "api_key", "apikey", "password", "secret", "authorization"}
20
+
21
+
22
+ def _redact(value: Any) -> Any:
23
+ if isinstance(value, dict):
24
+ return {
25
+ k: ("***REDACTED***" if k.lower() in _SENSITIVE_KEYS else _redact(v))
26
+ for k, v in value.items()
27
+ }
28
+ if isinstance(value, list):
29
+ return [_redact(v) for v in value]
30
+ if isinstance(value, str) and len(value) > 2000:
31
+ return value[:2000] + f"...[truncated {len(value) - 2000} chars]"
32
+ return value
33
+
34
+
35
+ class AuditLog:
36
+ """Thread-safe JSONL audit logger."""
37
+
38
+ def __init__(self, path: str | os.PathLike[str], human_owner: str = "unknown") -> None:
39
+ self.path = Path(path)
40
+ self.human_owner = human_owner
41
+ self._lock = threading.Lock()
42
+ if self.path.parent and not self.path.parent.exists():
43
+ self.path.parent.mkdir(parents=True, exist_ok=True)
44
+
45
+ def record(
46
+ self,
47
+ tool: str,
48
+ arguments: dict[str, Any],
49
+ decision: str,
50
+ status: str,
51
+ *,
52
+ risk: str | None = None,
53
+ detail: str | None = None,
54
+ ) -> dict[str, Any]:
55
+ entry = {
56
+ "timestamp": datetime.now(timezone.utc).isoformat(),
57
+ "human_owner": self.human_owner,
58
+ "agent": "superuser",
59
+ "tool": tool,
60
+ "arguments": _redact(arguments),
61
+ "risk": risk,
62
+ "decision": decision,
63
+ "status": status,
64
+ "detail": detail,
65
+ }
66
+ line = json.dumps(entry, ensure_ascii=False)
67
+ with self._lock:
68
+ with self.path.open("a", encoding="utf-8") as fh:
69
+ fh.write(line + "\n")
70
+ return entry