⚠ Not built. SG/Sentinel is a published design from May 2026 — “this is how I would build it” — not a product. No plans to build it unless somebody funds it. Read the note →
sg-sentinel.sgit.ai / try it

The sg sentinel CLI

Everything below is verbatim from the MVP testing manual — the commands the May 2026 prototype exercise answered to. The offline stack (targets B and C) needed no AWS account at all: the real L1 JS engine plus the real Python L2 actor, writing to a local sink.

Read before typing. SG/Sentinel has not been built as a product: the sg CLI these commands belong to is not published or packaged for you to install, and the prototype behind them is an unmaintained design artefact. This page documents how the prototype was exercised — as part of "this is how I would build it" — not an installation guide.

The command surface

sg sentinel rules   list | show <id> | test          # the six tiny-core rules
sg sentinel local   up [--direct] | status | hit [METHOD] <path> [--ip] [--direct] | down
                                                     # CF-env sim by default; --direct = node
sg sentinel logs    ls | tail [-n N] | trace <request-id>      # use case 1 (read the sink)
sg sentinel blocks  list | why <request-id|ip>                 # use case 2
sg sentinel deploy  create | destroy <id> | teardown <id>      # live AWS (mutation-gated)
sg sentinel status                                             # what's deployed
sg sentinel tui     rules|logs|blocks|status|traffic           # Textual operator screens
sg sentinel tui     api | chat "<q>" | dashboard               # TUI API + LLM chat (read-only)
sg sentinel traffic cases | gen | send --url <base>            # use-case corpus + measurement
sg sentinel echo    serve [--port N]                           # httpget echo origin

Every command supports --json. Mutating deploy commands take --yes / --dry-run, print the AWS context banner, and require the mutation gate. sn is the short alias.

See the engine without running it

$ sg sentinel rules list                 # table of the 6 MVP rules (id, name, action, MITRE tag)
$ sg sentinel rules show 0012            # one rule's metadata
$ sg sentinel rules test                 # runs the real node L1 engine over the canonical set

rules test drives the actual sentinel_l1.js through Node over the canonical request set and renders the six expected decisions — the same table on the front page.

Targets B & C — the offline stack

The CF-env simulation container (Target C) is the default; add --direct to run the engine via local node (Target B) instead. hit assumes GET unless you give a method; --ip spoofs the source IP.

$ sg sentinel local status               # what's ready: node / docker / container / sink + counts
$ sg sentinel local up                   # start the CF-env sim container (default)

# use case 2 — blocking (obvious-bad)
$ sg sentinel local hit /etc/passwd     --ip 185.10.10.10   # block, rule 0012, HTTP 403
$ sg sentinel local hit /wp-login.php   --ip 91.20.20.20    # block, rule 0018, HTTP 404
$ sg sentinel local hit /.env           --ip 77.30.30.30    # block, rule 0014, HTTP 404
$ sg sentinel local hit /index.html     --ip 10.0.0.6       # block, rule 0003 (banned ip)
$ sg sentinel local hit GET /index.html --ip 198.51.100.2   # allow, rule 0001

# use case 1 — read the sink
$ sg sentinel logs ls
$ sg sentinel logs tail -n 5
$ sg sentinel logs trace <request-id>
$ sg sentinel blocks list                # blocked requests + reason
$ sg sentinel blocks why 185.10.10.10    # raw IP works — matches the hashed store

$ sg sentinel local down                 # stop the container + clear the sink (--keep-logs to keep)

Because the CF-env sim is a real HTTP listener, you can also curl it directly — it runs L1 over the actual request and returns the signal:

$ curl -s http://127.0.0.1:8599/etc/passwd
{"verdict":"block","rule_id":"0012",…}
$ curl -s -H 'X-Forwarded-For: 10.0.0.6' http://127.0.0.1:8599/   # block, rule 0003 (banned ip)

Notes from the manual: source IPs are hashed in the stored record by default (privacy mode) — blocks why understands both the raw IP and its hashed form. --direct needs node on PATH; the default needs a running Docker daemon. SG_SENTINEL__LOCAL_SINK_DIR overrides the sink location.

Target A — live AWS (ephemeral CloudFront + Lambda@Edge)

Mutation-gated, and honestly labelled. This path creates real AWS resources and costs money — and it is the leg of the MVP that is code-complete and unit-tested via in-memory doubles but has not yet been run against a real CloudFront distribution. The commands below are the intended, implemented flow; the first live run is an open item on comms.
$ export SG_AWS__SENTINEL__ALLOW_MUTATIONS=1            # required for create/destroy/teardown
$ eval $(sg aws credentials switch <role>)              # real creds in this shell

$ sg sentinel deploy create --dry-run --region us-east-1   # preview first (no AWS calls)
$ sg sentinel deploy create --region us-east-1 --yes       # S3 + CF Function (L1) + L@E (L2), TTL 0
$ sg sentinel status

# the distribution takes ~15 min to deploy globally; then:
$ curl -sI https://<dXXXX>.cloudfront.net/etc/passwd    # HTTP/2 403
$ curl -sI https://<dXXXX>.cloudfront.net/.env          # HTTP/2 404
$ curl -sI https://<dXXXX>.cloudfront.net/index.html    # 200 / origin

$ sg aws s3 ls s3://<log-bucket>/sentinel/              # same sink layout as local
$ sg sentinel deploy teardown <distribution-id> --bucket <log-bucket> --yes   # no orphans

The automated test suite

$ python -m pytest tests/unit/sgraph_ai_service_playwright__cli/sentinel/ -q -rs
# everything (local-direct + in-memory AWS lifecycle); docker/live legs skip cleanly

$ python -m pytest tests/unit/sgraph_ai_service_playwright__cli/sentinel/parity/ -q -rs
# the three-target parity matrix (local-direct baseline always runs)
Opt-in legEnable with
B↔C docker paritya running docker daemon
AWS paritySG_SENTINEL__LIVE_TESTS=1 + SENTINEL_TEST_DISTRIBUTION=<cf-domain>
Live smoke (deploy→curl→teardown)SG_SENTINEL__LIVE_TESTS=1 + SG_AWS__SENTINEL__ALLOW_MUTATIONS=1

Operator TUIs and the read-only chat

The MVP grew past the CLI: Textual operator screens over the same data, each with --json and a plain-text no-TTY fallback, plus a structured TUI API and a read-only LLM chat grounded in live state.

$ sg sentinel tui rules        # the 6 rules; enter → detail (schema in/out)
$ sg sentinel tui logs         # records in the sink; enter → trace by request id
$ sg sentinel tui blocks       # blocks grouped by reason/rule + action breakdown
$ sg sentinel tui status       # reality + the EXACT materialised L1 engine code
$ sg sentinel tui traffic      # press g: replay the corpus through L1+L2 → accuracy + latency

$ sg sentinel tui api list                              # the read actions (no LLM, no AWS)
$ sg sentinel tui api invoke sg-sentinel blocks_why --params '{"needle":"185.10.10.10"}'

$ sg sentinel tui chat "why was 185.10.10.10 blocked?"  # Bedrock Nova micro; needs AWS creds
$ sg sentinel tui dashboard                             # all surfaces + one shared chat session

How the chat stays honest: it can only call the read actions, so answers are grounded in the real sink, rules and engine — and it changes nothing. Each answer shows the tool calls used and the model cost. Mutating actions are deliberately not exposed to chat.

Measuring the impact: traffic generator + echo origin

$ sg sentinel traffic cases                 # the labelled corpus: benign + malicious + malformed
$ sg sentinel traffic gen                   # through the real L1+L2 → accuracy 100%, 10/10 malicious blocked

$ sg sentinel echo serve --port 8080 &      # the httpget origin (GET /__hits = what reached it)
$ sg sentinel traffic send --url http://127.0.0.1:8080   # BARE origin → 0/10 malicious blocked
$ curl -s http://127.0.0.1:8080/__hits      # confirm /etc/passwd, /.env … reached the bare origin

The contrast between a Sentinel-fronted target (malicious blocked, only good traffic reaches origin) and the bare origin (everything reaches it) is the impact measurement. Note the manual's own caveat: gen latency is harness cost, not CloudFront runtime — real edge latency needs send against a live distribution.