{"id":49841936,"url":"https://github.com/mattwilkinsonn/sentinel","last_synced_at":"2026-06-09T22:30:25.676Z","repository":{"id":347610713,"uuid":"1193193814","full_name":"mattwilkinsonn/sentinel","owner":"mattwilkinsonn","description":"Decentralized threat intelligence network for EVE Frontier on Sui.","archived":false,"fork":false,"pushed_at":"2026-05-11T05:17:47.000Z","size":128957,"stargazers_count":1,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-30T21:03:58.887Z","etag":null,"topics":["blockchain","evefrontier","move","rust","sst","sui","typescript"],"latest_commit_sha":null,"homepage":"https://sentinel.zireael.dev/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mattwilkinsonn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-27T01:05:07.000Z","updated_at":"2026-05-11T05:16:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mattwilkinsonn/sentinel","commit_stats":null,"previous_names":["mattwilkinsonn/sentinel"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mattwilkinsonn/sentinel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattwilkinsonn%2Fsentinel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattwilkinsonn%2Fsentinel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattwilkinsonn%2Fsentinel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattwilkinsonn%2Fsentinel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mattwilkinsonn","download_url":"https://codeload.github.com/mattwilkinsonn/sentinel/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattwilkinsonn%2Fsentinel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34129072,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blockchain","evefrontier","move","rust","sst","sui","typescript"],"created_at":"2026-05-14T07:01:36.527Z","updated_at":"2026-06-09T22:30:25.652Z","avatar_url":"https://github.com/mattwilkinsonn.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# SENTINEL\n\n[![CI](https://github.com/mattwilkinsonn/sentinel/actions/workflows/ci.yml/badge.svg)](https://github.com/mattwilkinsonn/sentinel/actions/workflows/ci.yml)\n\nDecentralized threat intelligence network for\n[EVE Frontier](https://evefrontier.com) on\n[Sui](https://sui.io).\n\nStreams on-chain events, computes threat scores, publishes\nresults to an on-chain registry, and provides a real-time\ndashboard. Smart Gates can autonomously block high-threat\npilots based on SENTINEL scores.\n\nBuilt for the EVE Frontier x Sui Hackathon 2026.\n\n## Demo\n\n[![SENTINEL demo](https://img.youtube.com/vi/tdI8pDOWx44/maxresdefault.jpg)](https://www.youtube.com/watch?v=tdI8pDOWx44)\n\n## Architecture\n\n```text\nSui Blockchain (gRPC checkpoint stream)\n        |\n        v\n+------------------+      +------------------+\n| Rust Backend     |-----\u003e| Postgres         |\n| (Axum + Tokio)   |      | (Neon / Docker)  |\n+------------------+      +------------------+\n   |  REST + SSE\n   v\n+------------------+\n| Solid.js         |\n| Dashboard        |\n+------------------+\n```\n\n**On-chain:** Move smart contracts for threat registry\nand bounty board.\n\n**Backend:** Rust/Tokio service with true multi-threaded\nconcurrency. Spawns parallel tasks for gRPC checkpoint\nstreaming, historical data loading, on-chain publishing,\nname/system resolution, DB persistence, and Discord bot —\nall running across multiple CPU cores simultaneously. SSE\npushes events to the frontend the instant a checkpoint is\nprocessed.\n\n**Frontend:** Solid.js dashboard with fine-grained reactivity\n(no virtual DOM diffing). Real-time threat leaderboard,\nevent feed, kill stats, system intelligence, and earned titles.\n\n**Discord bot:** Serenity-based bot with slash commands for\nquerying pilot threat scores, leaderboards, kills, system\nactivity, and live events. Sends real-time CRITICAL threat\nalerts to configured channels.\n\n### Concurrency model\n\nThe backend runs multiple Tokio tasks in parallel:\n\n- **gRPC stream** — ingests Sui checkpoints in real-time;\n  reconnects automatically on disconnect\n- **Historical loader** — seeds profiles and structure data\n  from Sui GraphQL on startup; runs in background so the\n  API is immediately responsive\n- **On-chain publisher** — batches threat scores to the\n  ThreatRegistry contract every 30s; only publishes profiles\n  that changed beyond a configurable threshold\n- **Name resolver** — resolves pending character names via\n  gRPC object lookups every 10s\n- **Metadata resolver** — resolves system names, tribe\n  affiliations, and structure type names via World REST API\n- **DB sync loop** — flushes dirty profiles and events to\n  Postgres every 5s\n- **Discord bot** — runs as an independent Tokio task;\n  reads shared `AppState` directly with no extra IPC\n- **Health monitor** — logs stream staleness and full\n  health summaries; warns if no checkpoint in \u003e2 minutes\n- **Demo event loop** — generates realistic fake events\n  so the dashboard is usable without live chain activity\n- **HTTP server** — serves REST API + SSE concurrently\n\nUnlike single-threaded async runtimes, Tokio distributes\ntasks across all available CPU cores. The gRPC stream can\nprocess a checkpoint on one core while the API serves a\nrequest on another — true parallelism, not cooperative\nmultitasking.\n\nShared state uses `Arc\u003cRwLock\u003cAppState\u003e\u003e` with a strict\nshort-lock discipline:\n\n- **Readers never block readers.** Multiple API requests\n  read threat data simultaneously with zero contention.\n- **Writers hold locks for microseconds.** The gRPC handler\n  locks, updates one profile's stats, unlocks — then moves\n  to the next checkpoint. No lock is ever held across a\n  network call, disk write, or `.await` point.\n- **Expensive I/O happens outside locks.** The DB sync loop\n  snapshots dirty profiles under a brief write lock, then\n  performs all Postgres upserts with no lock held. The\n  historical loader resolves gate names via GraphQL\n  (network I/O) before acquiring the state lock to insert.\n- **Background tasks are independent.** The gRPC stream,\n  publisher, metadata resolver, and DB sync loop each\n  acquire locks independently — a slow World API response\n  in the metadata resolver never blocks the gRPC stream\n  from processing the next checkpoint.\n\nHistorical data loads in the background so the API is\nresponsive immediately on startup.\n\n## Tech Stack\n\n| Layer           | Tech                                           |\n| --------------- | ---------------------------------------------- |\n| Smart Contracts | Move (Sui)                                     |\n| Backend         | Rust, Axum, Tokio, SQLx, tonic (gRPC)          |\n| Discord Bot     | Serenity (Rust)                                |\n| Database        | PostgreSQL 16 (Neon prod, Docker dev)          |\n| Frontend        | Solid.js, TailwindCSS 4, Vite                  |\n| Infrastructure  | AWS ECS Fargate Spot, API Gateway, CloudFront, Pulumi |\n| CI/CD           | GitHub Actions                                 |\n| Linting         | Biome (TS), rustfmt (Rust), Prettier (Move)    |\n\n## Quick Start\n\n### Prerequisites\n\n- [Rust](https://rustup.rs/)\n- [Bun](https://bun.sh/)\n- [Docker](https://docs.docker.com/get-docker/)\n- [Just](https://github.com/casey/just)\n- [Sui CLI](https://docs.sui.io/guides/developer/getting-started/sui-install)\n- [pre-commit](https://pre-commit.com/)\n\n### Setup\n\n```bash\n# Install dependencies\njust install\n\n# Install git hooks\npre-commit install\n\n# Copy and fill in environment variables\ncp .env.example .env\n```\n\n### Development\n\n```bash\n# Start Postgres + backend + frontend\njust dev\n\n# Backend: http://localhost:3001\n# Frontend: http://localhost:5173\n```\n\nOr run components individually:\n\n```bash\njust db              # Start Postgres\njust backend-run     # Start backend (needs DATABASE_URL)\njust frontend-dev    # Start frontend dev server\n```\n\n### Testing\n\n```bash\njust backend-test              # Rust unit tests\njust backend-test-integration  # Postgres integration tests\njust frontend-test             # Frontend tests\njust contracts-test            # Move contract tests\n```\n\n### Code Quality\n\n```bash\njust fmt       # Format everything\njust lint      # Lint TypeScript (Biome)\njust check     # Verify all formatting + linting\n```\n\n## Project Structure\n\n```text\nsentinel/\n  move-contracts/\n    sentinel/          # Threat registry + smart gate\n    bounty_board/      # Bounty board contract\n  sentinel-backend/    # Rust backend service\n    src/\n      api.rs           # REST API + SSE endpoints\n      config.rs        # Environment config (panics on missing vars)\n      db.rs            # Postgres persistence + migrations\n      demo.rs          # Demo mode (realistic fake events)\n      discord.rs       # Discord bot (serenity) — slash commands + alerts\n      grpc.rs          # Checkpoint streaming + killmail/event handlers\n      historical.rs    # GraphQL historical loader (characters, structures)\n      names.rs         # Character name resolution via gRPC\n      publisher.rs     # On-chain threat score publisher\n      sui_client.rs    # Shared Sui gRPC utilities\n      threat_engine.rs # Threat scoring + tiers + earned titles\n      types.rs         # AppState, ThreatProfile, DataStore\n      world_api.rs     # World REST API (system names, tribes, type names)\n    migrations/        # SQL schema (auto-applied on startup)\n    tests/             # Integration tests (DB, gRPC mock, GraphQL mock)\n  frontend/            # Solid.js dashboard\n    src/\n      SentinelDashboard.tsx  # Main dashboard\n      SentinelFeed.tsx       # Live event feed\n      StatsBar.tsx           # Aggregate stats bar\n      ThreatCard.tsx         # Pilot threat card\n      ThreatLeaderboard.tsx  # Threat leaderboard\n      views/                 # Sub-views (feed, pilots, kills, systems)\n  ts-scripts/          # Admin scripts\n  infrastructure/      # Pulumi IaC (TypeScript)\n  .github/workflows/   # CI/CD pipeline\n```\n\n## API Endpoints\n\n| Endpoint             | Method | Description                     |\n| -------------------- | ------ | ------------------------------- |\n| `/api/data`          | GET    | Combined demo + live data       |\n| `/api/events/stream` | SSE    | Real-time event stream          |\n| `/api/health`        | GET    | Health check + profile counts   |\n\n## Discord Bot\n\nSENTINEL includes a Discord bot for querying threat data and receiving\nreal-time alerts without opening the dashboard.\n\n### Slash commands\n\n| Command        | Description                                                        |\n| -------------- | ------------------------------------------------------------------ |\n| `/threat`      | Look up a pilot — threat score, tier, K/D, titles, recent kills    |\n| `/leaderboard` | Top 10 pilots by threat score with tier medals                     |\n| `/kills`       | Last 10 kills with killer, victim, system, and timestamp           |\n| `/systems`     | Top 10 most active solar systems by pilot count                    |\n| `/events`      | Last 10 live events from the feed                                  |\n| `/stats`       | Aggregate network stats (pilots tracked, kills, bounties)          |\n| `/alerts`      | Configure CRITICAL threat alerts for your server                   |\n\n### Alerts\n\nUse `/alerts set #channel` to designate a channel for automatic\nCRITICAL-tier threat notifications. SENTINEL posts an embed whenever\na pilot crosses the CRITICAL threshold (score \u003e 75.00), including\ntheir stats, earned titles, and a threat score progress bar.\nAlert channel configuration is persisted in Postgres and survives restarts.\n\n### Bot setup\n\nSet `DISCORD_TOKEN` in your environment to a valid bot token. The bot\nregisters slash commands globally on startup.\n\n## Threat Scoring\n\nScores range from 0-100.00 (stored internally as 0-10,000\nbasis points, displayed divided by 100) across five factors:\n\n| Factor           | Max    | Formula              |\n| ---------------- | ------ | -------------------- |\n| Recency (24h)    | 3,500  | recent_kills * 600   |\n| Kill count       | 2,000  | log2(kills+1) * 600  |\n| K/D ratio        | 1,500  | kd * 400             |\n| Bounties         | 1,500  | bounty_count * 500   |\n| Movement         | 500    | systems_visited * 100|\n\nThreat tiers: LOW (0-25), MODERATE (25.01-50),\nHIGH (50.01-75), CRITICAL (75.01+).\n\n## AI-Assisted Development\n\nSENTINEL was built during a 2-week hackathon using Claude as a development\npartner. The entire backend, frontend, infrastructure, and smart contracts\nwere written through human-AI collaboration.\n\n### How it worked\n\n[@mattwilkinsonn](https://github.com/mattwilkinsonn) directed all architecture\ndecisions, technology choices, and technical corrections. Claude handled\nimplementation — writing code, debugging, and executing the direction given.\nTechnical decision came from Matt; Claude's role was to\nimplement it quickly and correctly.\n\nThis is not \"AI wrote the app.\" It's closer to having an engineer\nwho types very fast and never gets tired — but still needs a tech lead to\nmake the right calls.\n\n### Parallel agents\n\nFor independent workstreams, multiple Claude instances ran in parallel:\n\n- One implementing a backend feature while another wrote frontend components\n- One debugging a CI failure while another worked on smart contracts\n- One writing tests while another refactored the data model\n\nThis required careful coordination to avoid merge conflicts on shared files\nlike `types.rs` and `AppState`.\n\n### Short-held locks\n\nOne concrete example of this collaboration was shaping architecture: the\n`Arc\u003cRwLock\u003cAppState\u003e\u003e` lock discipline. Claude's initial implementation\nheld write locks across async I/O. Matt caught this and directed a\nrewrite where:\n\n- Expensive operations (GraphQL, DB writes, World API calls) happen outside locks\n- Write locks are held for microseconds — only for in-memory state mutations\n- The DB sync loop snapshots dirty state under a brief lock, then flushes to Postgres unlocked\n\nThis pattern is documented in the Architecture section above and is the\nreason the backend handles concurrent load without contention.\n\n### Example prompts\n\nThese are real prompts from the development session, showing the kind of\ndirection that shaped the system:\n\n#### Architecture \u0026 technology choices\n\n- *\"Work on postgres persistence. Set up docker compose for local testing.\"*\n- *\"Is there anything we can do utilizing this better stack?\"*\n- *\"first let's get this deployed. next we can get this on the real net\"*\n\n#### Technical corrections\n\n- *\"I think it should just fail if no postgres — makes rollbacks easier if AWS catches the container not healthy\"*\n- *\"We shouldn't have default URLs imo, just panic when the URLs aren't set as env vars\"*\n- *\"Waiting seconds is flaky. can we query the endpoint and get a return value instead?\"*\n- *\"Just remove it entirely. no need to support the in-memory when we can easily spin up docker containers.\"*\n\n#### Bug reports\n\n- *\"Frontend still renders kills as ? killed ? and the other fields have [OBJECT OBJECT]\"*\n- *\"Still getting duplicate kills after wiping db and running backend twice\"*\n- *\"Top system card on Live server feed, still showing system ID not system name\"*\n- *\"still 8 chars with pending names. likely a bug still with our resolution somehow.\"*\n\n#### Design decisions\n\n- *\"Use B\"* (choosing OIDC over static AWS keys for CI)\n- *\"I dont want to disable truthy entirely, can we just add an inline comment ignore?\"*\n- *\"Can we remove the ! for tests\"* (eliminating non-null assertions)\n\n#### Feature direction\n\n- *\"Can we just wait to send the event with a new char name until we resolve that name?\"*\n- *\"Can we confirm they are STRUCTURE kills via the ID range or another method? it would be nice to have them in the live feed\"*\n- *\"Mini live event feed — make it display as many events to match the length of the content next to it\"*\n\n#### Infrastructure\n\n- *\"Change to ARM. Doesn't GitHub have a container registry we can use?\"*\n- *\"Should the API be under api.sentinel.zireael.dev?\"*\n- *\"Can we just use ARM CI runners instead of cross compilation?\"*\n\n## Deployment\n\n### Local (Docker Compose)\n\n```bash\ndocker compose up --build\n```\n\n### AWS (Pulumi)\n\nDeploys automatically via GitHub Actions on push to `main` (production)\nand `dev` (dev environment). Infrastructure is defined in\n`infrastructure/src/` using Pulumi TypeScript.\n\n```bash\njust deploy-preview      # Preview production changes\njust deploy-preview-dev  # Preview dev changes\n```\n\nRequires Pulumi Cloud access token and AWS/Cloudflare/Neon secrets\nconfigured in GitHub Actions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattwilkinsonn%2Fsentinel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmattwilkinsonn%2Fsentinel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattwilkinsonn%2Fsentinel/lists"}