- TypeScript 95%
- CSS 2.2%
- Dockerfile 2%
- JavaScript 0.5%
- HTML 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- Updated `compose.linux.yml` to set user and group IDs for the wg-agent service, ensuring effective NET_ADMIN capabilities while maintaining proper Unix DAC. - Adjusted directory and socket permissions to 0770 and 0660 respectively, allowing shared GID access. - Modified Dockerfile to reflect new permissions and ownership settings for runtime directories. - Updated documentation to clarify access models and required environment variables. - Enhanced tests to validate new permission settings and ensure security posture. |
||
| .forgejo/workflows | ||
| apps | ||
| config | ||
| docs | ||
| infrastructure | ||
| packages | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc.json | ||
| AGENTS.md | ||
| compose.linux.yml | ||
| compose.yaml | ||
| Dockerfile | ||
| eslint.config.js | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.base.json | ||
| vitest.config.ts | ||
Shoshskie Sasy
Self-hosted open-source control plane for monitoring (and later recovering) WireGuard / AmneziaWG tunnels between arbitrary nodes.
The app is topology-agnostic: you define nodes and links in configuration. It does not assume home routers, a specific VPS, geography, or a fixed tunnel direction.
v0.1 is observe-only. It reads WireGuard state, stores history, and shows a dashboard. It does not change ports, firewalls, routers, or interfaces.
How WireGuard health is determined
A successful UDP probe is NOT considered proof that a WireGuard tunnel is healthy.
The primary signal is a fresh authenticated WireGuard handshake on the configured peer:
| Status | Meaning |
|---|---|
healthy |
Handshake age ≤ staleAfterSeconds |
stale |
Handshake exists but is older than the threshold |
never_connected |
Handshake epoch is 0 / absent |
error |
Interface unreadable, expected peer missing, or other read failure |
Health is always computed from the link’s endpoints.observed interface and the peer matching peerPublicKey (exact match, never by dump index).
Architecture
apps/server Fastify API + polling + (production) static UI
apps/web React + Vite dashboard (dev server / built assets)
packages/* config, core, wireguard, storage
- Backend = control plane; Linux/WireGuard kernel = data plane.
- No WireGuard protocol implementation in JavaScript.
- Production prefers one Node process serving API + built UI.
Requirements
- Node.js ≥ 22
- pnpm ≥ 9 (pnpm 11+:
allowBuildsinpnpm-workspace.yaml) - Linux +
wireguard-tools(wg) forprovider: linux
Configuration precedence
SHOSHSKIE_SASY_CONFIG(absolute path recommended) — preferred in production
Legacy alias:SHOSHSKIE_CONFIGconfig/config.local.yamlif present (gitignored)config/config.example.yaml— development only
With NODE_ENV=production, missing a real config is a startup error. The app will not silently use the example file.
Create a private config:
cp config/config.local.template.yaml config/config.local.yaml
# edit placeholders — never commit this file
Observe-only Link shape
links:
tunnel-a:
name: Example Tunnel
endpoints:
observed:
node: observe-host
interface: wg0
peerPublicKey: REPLACE_WITH_PEER_PUBLIC_KEY
peer:
node: edge-a
health:
staleAfterSeconds: 180
peerPublicKey is a WireGuard public key (not a secret) and is required on the observed endpoint.
SQLite path:
app:
databasePath: /var/lib/shoshskie-sasy/shoshskie-sasy.db
Self-hosted Git / Forgejo
Shoshskie Sasy does not depend on a specific Git hosting provider. A private Forgejo (or any other Git host) works without application changes.
- Clone, build, and deploy from any Git remote.
- Optional CI:
.forgejo/workflows/ci.ymlruns install/build/typecheck/test/lint via Forgejo Actions (no deploy, no secrets). - Adjust the workflow
runs-onlabel if your Forgejo runners use a different name thandocker.
Local development
pnpm install
pnpm build
pnpm test
pnpm typecheck
pnpm lint
pnpm diagnose
pnpm dev
- API/UI (dev): Vite on
:5173proxies/api→ Fastify:8787 - Fake provider is the default in
config.example.yaml
Observe-only deployment
Recommended: Docker Compose with privilege-separated wg-agent — see docs/docker.md.
Alternative: native/systemd (infrastructure/systemd/shoshskie-sasy.service.example).
| Docker + agent | Native / systemd | |
|---|---|---|
| Pros | Reproducible images, app isolation, clean upgrades | Direct wg access, fewer moving parts |
| Cons | Host-network agent needs CAP_NET_ADMIN |
Host Node/runtime deps |
Native flow
- Clone the Git repository on the Linux VPS
pnpm install&&pnpm build- Create private config
NODE_ENV=production+SHOSHSKIE_SASY_CONFIG=...pnpm diagnosepnpm start/ systemd
Docker flow (summary)
# Safe fake demo
docker compose up --build -d
# Linux production (private config required)
cp config/docker.local.template.yaml config/docker.local.yaml
cp .env.example .env # SHOSHSKIE_CONFIG_FILE=./config/docker.local.yaml
# edit docker.local.yaml — do not commit
docker compose -f compose.yaml -f compose.linux.yml build
docker compose -f compose.yaml -f compose.linux.yml run --rm --no-deps --entrypoint node app dist/data-preflight-cli.js
docker compose -f compose.yaml -f compose.linux.yml run --rm --no-deps --entrypoint node wg-agent dist/index.js --preflight
docker compose -f compose.yaml -f compose.linux.yml up -d
Host publish: 127.0.0.1:8787 only. Details: docs/docker.md.
Secrets: wg show … dump private keys / PSKs are stripped inside wg-agent and must never appear in the agent protocol, API, SQLite, or logs.
Linux WireGuard agent currently requires rootful Docker (host network + CAP_NET_ADMIN).
Forgejo Container Registry (optional)
Workflow: .forgejo/workflows/container.yml (tag v* / manual).
Configure:
| Name | Type | Purpose |
|---|---|---|
DOCKER_REGISTRY |
variable | Registry hostname (optional; defaults to Forgejo host) |
REGISTRY_USER |
secret | Registry username |
REGISTRY_TOKEN |
secret | Token with package write scope |
Images: shoshskie-sasy-app, shoshskie-sasy-agent. Registry may be disabled on some Forgejo instances — local docker compose build still works.
Permissions
wg show <interface> dump is read-only, but access control is distro-specific.
Typical options (choose one; do not run the whole app as root unless you have proven it is required):
- Run the service user in a group that can read WireGuard state (if your distro provides one)
- Use file capabilities / policy carefully if your environment requires it
- Document the chosen approach in your private runbook
Shoshskie Sasy never calls wg set, wg-quick, iptables/nft, or interface mutations in v0.1.
Security
- Observe-only; no mutation commands
execFilewith argument arrays (no shell string building)- Interface names and link IDs come from validated config only
- Bind loopback by default
- Do not log private keys / PSKs / credentials
- Domain events ≠ technical logs
Diagnostics
pnpm diagnose
Checks config load, database path, wg binary (for linux provider), and each link’s interface/peer/handshake — without changing anything.
API (read-only)
| Method | Path | Purpose |
|---|---|---|
| GET | /api/healthz |
Process / DB liveness (Docker HEALTHCHECK) |
| GET | /api/health |
Aggregate WireGuard link health |
| GET | /api/system |
Version, build metadata, paths, counts, uptime |
| GET | /api/nodes |
Nodes |
| GET | /api/links |
Links + last health |
| GET | /api/links/:id |
Link details |
| GET | /api/links/:id/history |
Samples + events |
| GET | /api/events |
Event feed |
Roadmap
| Version | Focus |
|---|---|
| v0.1 | Monitoring / observe-only |
| v0.2 | Manual recovery + dry-run |
| v0.3 | Automatic port rotation |
| v0.4 | Keenetic adapter |
| v0.5 | Notifications |
License
To be chosen before the first public release.