dumbtv
dumbtv가 ADB로 Fire TV에서 광고·추적·업데이트 알림을 제거, 루트·앱 변경 없이, 무료 오픈소스
댓글을 불러오는 중입니다.
로그인 상태를 확인하는 중입니다.
Self-hosted 3D map of your information systems: zones, VLANs, assets, flows, exposure, vulnerabilities and incident contacts. Connectors for GLPI, phpIPAM, Cyberwatch and Palo Alto.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Strata is a self-hosted 3D map of your information systems (IS): zones, VLANs, low-poly assets, inbound and outbound flows, port exposure (Internet, inter-VLAN, intra-VLAN), vulnerabilities and incident contacts. Website and documentation: https://solidgeeks.github.io/strata/
Website and documentation: the docs/ folder holds the GitHub Pages site, with the scroll-driven presentation film. To publish it: Settings › Pages › Build and deployment › Deploy from a branch, branch main, folder /docs. The film's source code is in media/reel/.
Strata is on its way to 1.0.0: from then on, the API, the export format, the .env settings, the Docker image and scripts/users.mjs only break in a new major version.
| Milestone | What it brings |
|---|---|
| 0.7.0 ✅ | Hardening and tests: limit failed sign-ins, a security review, a browser test of the interface. Released on 2026-10-26 |
| 0.8.0 ✅ | Connector modules and a module store, three.js 0.186. Released on 2026-10-26 |
| 0.9.0 ✅ | The last breaking changes: single sign-on, pre-releases, an API reference, the old product name removed. Released on 2026-10-26 |
| 0.10.0 ✅ | Incident mode with Muster, the risk register with baselines, logs for the SIEM, the steering dashboard. Released on 2026-10-28 |
| 1.0.0 | Release candidates (1.0.0-rc.N), then 1.0.0 |
Follow it on the milestones page. Ideas for after 1.0 are discussed, and voted on, in Discussions. Want to help? Start with a good first issue.
npm run build:online): the whole interface without a server, the map kept in the browser.LOCAL_LOGIN keeps local accounts for everyone, for admins only (break-glass) or for no one. See section 4.Authorization: Bearer stk_….package.json after a maintainer's approval; release candidates (1.0.0-rc.N) are never latest.X-Requested-With: strata, and DATA_VOLUME is gone.Connectors are now modules: a folder with a manifest (strata-module.json: name, settings, what it may reach) and its code. The four connectors that ship with Strata are modules like any other, and anyone can write a new one without touching Strata's code: see MODULES.md.
nmap -oX), both imported by any contributor as a proposal, and NetBox (devices, virtual machines, VLANs and prefixes, synced with a read-only token).CONNECTOR_TIMEOUT_MINUTES. Whatever it reads still arrives as a proposal an approver reviews.MODULES_DIR (a Docker volume, or services.strata.modules on NixOS), or a package file when MODULE_UPLOAD=true. scripts/pack-module.mjs packs a module for the store.services.strata module (section 6, Nix and NixOS).429), and passwords can be read from files (EDIT_PASS_FILE, VIEW_PASS_FILE): section 3.Display › Theme: Auto follows the system's light or dark setting (the default), Day and Night force one. Keyboard shortcut: N switches between day and night. The choice is remembered in the browser. The 3D view, the 2D diagram and the panels follow the theme; exported architecture plans (PNG/SVG) stay on a light background, for documentation.
Four connectors, in Collaboration › Connectors (self-hosted version only). None of them writes to the map directly: each sync prepares one proposal per IS, which an approver reviews on the map (green / orange / red highlights) and then publishes or rejects, like a proposal made by hand. Until it is handled, the next sync updates the same proposal instead of piling up a new one. If the map already matches, no proposal is created.
| Connector | What it reads | What it proposes |
|---|---|---|
| Cyberwatch | servers and their CVEs (API v3: /api/v3/vulnerabilities/servers, then each server's details) | asset vulnerabilities, matched by host name, then by IP. Minimum severity and maximum count per asset can be set; CVEs ignored in Cyberwatch are left out by default. Cyberwatch "owns" the CVE-… entries of the assets it scans; findings entered by hand (Config.…) are kept. |
| GLPI | computers, network devices, printers, phones (REST API apirest.php) with their IP (network ports), OS, installed software, technician and group in charge, location | the location points to the IS (rules location text = IS id, or else the IS whose name appears in the location). Existing assets get their OS, applications and contacts (those entered by hand stay; an application entered by hand keeps its ports and takes the version from the inventory). Missing assets are created on the VLAN whose range contains their IP, or else on a To sort (from GLPI) plate in an Imported zone. |
| phpIPAM | sections, subnets, VLANs, addresses (API /api/{app}/…) | each IS's VLANs (number, name, range): a subnet already present as a VLAN of an IS goes to that IS, otherwise rules section or location = IS id. New VLANs arrive in the Imported zone. Addresses fill in asset IPs, move an asset to its subnet's VLAN, and flag IPs that differ (without changing them). Creating assets from named addresses: off by default. |
| Palo Alto | PAN-OS configuration of a firewall or of Panorama: an XML file imported in the app (Import a configuration file, open to contributor) or the XML API (type=export&category=configuration, key of a read-only admin role) | active allow rules become flows of the chosen IS: address objects and groups → assets by IP (a range is expanded into its assets up to a threshold, 12 by default), public addresses and FQDNs → existing Internet services (same host) or new ones, services and service groups → ports (application-default → the usual ports of common App-IDs), and the chosen firewall as the device crossed. An any source in an untrust/internet/outside/wan zone means "Internet". Inbound flows from the Internet to an administration or database port are flagged as risky. Flows of a rule that disappeared are removed (can be turned off); flows drawn by hand are never touched. What cannot be drawn (internal → rules, dynamic groups, negations, addresses without an asset) is listed in the proposal. |
Settings: an admin fills in each connector (Settings); Test the connection checks access; Sync now (approver) runs a sync in the background; Sync automatically every … hours schedules it. Credentials are stored on the server in data/connectors.json (mode 600), are never sent back to the browser, and are not in the map's JSON backups (but they are in the backup.sh archive: protect it). Internal certificates: paste your CA certificate in More settings, or tick Accept any TLS certificate on a trusted network.
Assets keep the link to their source record in ext (for example {"glpi":"Computer/12","cyberwatch":101}), which makes the next syncs reliable, even after a rename.
Four-eyes rule: a proposal from a Palo Alto file counts as the proposal of the person who imported it (they cannot approve it themselves). A live sync (data read from your tools) can be approved by the person who started it.
</> button).strata, for example), Read permission, security SSL with App code token; the application code is the token.https://firewall/api/?type=keygen); or let the connector generate the key from the user name and password.CONNECTORS=false in .env turns the connectors off completely.
License: Strata is free software under the GNU AGPL v3. You can use it, modify it and redistribute it; if you make a modified version available to users over a network, you must give them access to its source code. The Source code link at the bottom of the control panel points to SOURCE_URL (in .env): point it at your fork if you modify the code.
To try it without plugging in your tools: load the demo, run node scripts/connector-sim.mjs (with Docker: docker compose exec -d strata node scripts/connector-sim.mjs) (fake Cyberwatch, GLPI, phpIPAM and PAN-OS on port 8099, with data matching the Lyon IS) and use the addresses and credentials listed at the top of the script.
Named accounts and roles (self-hosted version):
| Role | Can |
|---|---|
viewer | browse the map, the asset sheets, the plans and the history |
contributor | also prepare proposals (drafts) and submit them |
approver | also edit the map directly, approve or reject proposals, restore a version |
admin | also manage accounts |
The first admin comes from EDIT_USER / EDIT_PASS (recreated or reset on every start, handy if you forget the password). Other accounts are managed in the app (Collaboration › Account) or from the command line: node scripts/users.mjs set alice contributor (with Docker: docker compose exec strata node scripts/users.mjs set alice contributor). Everyone changes their own password in Account. Passwords are stored hashed (scrypt) in data/users.json.
Version history and rollback: every change is recorded (who, when, what) in data/history.jsonl. Collaboration › History lists the changes, for the open IS or the whole map. View shows the map as it was (read-only, yellow banner), Restore puts the whole map back to that version. A restore is itself a version: it can be undone.
Proposals to review: a contributor clicks Propose a change, then edits the map as usual (2D diagram, sheets, forms). Everything goes into a draft (blue Draft banner); the real map does not move. Submit for review sends it to the approvers.
ALLOW_SELF_APPROVAL=true).vlans[].pos).The package contains the web app and a small Node.js server with no runtime dependencies:
strata/
├── public/ the app (index.html, app.js, app.css)
├── server/server.js the server (API + static files)
├── scripts/ vendor.mjs (fetches three.js and the fonts), users.mjs (accounts), backup.sh, restore.sh
├── deploy/ Caddyfile (HTTPS), strata.service (systemd)
├── tests/ API tests (npm test)
├── flake.nix, nix/ Nix package, NixOS module (services.strata) and its VM test
├── Dockerfile, docker-compose.yml, .env.example
└── package.json
One of these two options:
docker compose version), recommended;node --version);The machine needs access to the npm registry once, during the build, to download three.js and the fonts. After that, the app runs without Internet access.
Users need a browser with WebGL 2 and JavaScript modules: Chrome or Edge 80 and later, Firefox 114 and later, Safari 15 and later (three.js has needed WebGL 2 since 2024).
cd strata
cp .env.example .env
nano .env # change EDIT_PASS
docker compose up -d --build # builds the image here (needs the npm registry during the build)
On Windows (PowerShell or cmd): copy .env.example .env, then edit .env with Notepad (notepad .env).
Or use the prebuilt image published with each release (tag vX.Y.Z, see the Release workflow): docker compose pull && docker compose up -d fetches ghcr.io/solidgeeks/strata.
Every published image is signed: an attestation proves it was built by this repository's Release workflow, from the code of the tag. To check it before deploying (you need the GitHub CLI):
gh attestation verify oci://ghcr.io/solidgeeks/strata:0.10.0 --owner SolidGeeks
The command must print Verification succeeded!. If it fails, do not use the image, and report it (see SECURITY.md). Images older than 0.5.4 are not signed.
Then open http://localhost:8080 and sign in with EDIT_USER / EDIT_PASS.
The map starts empty. To explore it, click Load demo (5 IS). To start from scratch, click Delete everything.
Useful commands:
docker compose logs -f strata # logs
docker compose restart strata # restart
docker compose down # stop (the data stays in the volume)
By default, the port only listens on the machine itself (127.0.0.1:8080). To open it to your network, see section 5.
.env)| Variable | Default | Meaning |
|---|---|---|
EDIT_USER / EDIT_PASS | admin / empty | First admin account, recreated or reset on every start. With no EDIT_PASS and no account in data/users.json, there is no authentication at all. |
VIEW_USER / VIEW_PASS | viewer / empty | Shared read-only account, ignored while VIEW_PASS is empty. |
EDIT_PASS_FILE / VIEW_PASS_FILE | — | Read the password from this file instead (Docker secrets, systemd credentials, sops-nix, agenix). The file wins over the variable; a trailing newline is ignored. If the file cannot be read, the server does not start. |
ALLOW_SELF_APPROVAL | false | true lets an approver approve their own proposals. |
HISTORY_KEEP | 5000 | Number of changes kept in the history. |
READ_ONLY | false | Freezes the map for everyone. |
BACKUP_KEEP | 14 | Number of automatic backups kept. |
BACKUP_EVERY_HOURS | 24 | How often automatic backups are made. |
MAX_IMPORT_MB | 20 | Maximum size of an imported file. |
LOGIN_MAX_FAILS | 10 | Different wrong passwords for one account before its sign-ins are refused (429) for LOGIN_LOCK_MINUTES. 0 turns it off. |
LOGIN_MAX_FAILS_PER_ADDRESS | 20 | The same, for all the accounts tried from one client address. 0 turns it off. |
LOGIN_LOCK_MINUTES | 15 | How long a lock lasts; failures are counted over the same time. |
TRUST_PROXY | — | Reverse proxies whose X-Forwarded-For gives the client address: addresses, IPv4 ranges (), or , comma-separated. Unset: the address connecting to Strata is the client (section 5). |
After changing .env, apply it with docker compose up -d.
Local accounts (EDIT_PASS, scripts/users.mjs, Account) keep working. Single sign-on comes next to them:
OpenID Connect (Microsoft Entra ID, Keycloak, Authentik, Google Workspace, Okta…). Register Strata at your provider as a web application with the redirect URI https://strata.example.org/auth/oidc/callback, then:
PUBLIC_URL=https://strata.example.org # the address users open; HTTPS
SSO_ISSUER=https://login.microsoftonline.com/<tenant id>/v2.0
SSO_CLIENT_ID=<application id>
SSO_CLIENT_SECRET_FILE=/run/secrets/strata-sso # or SSO_CLIENT_SECRET=
SSO_NAME=Microsoft Entra ID
SSO_ROLE_ADMIN=strata-admins # groups, comma-separated; the highest role wins
SSO_ROLE_APPROVER=soc
SSO_ROLE_CONTRIBUTOR=it-ops
SSO_DEFAULT_ROLE=viewer # everyone else; empty: no access
Opening Strata then shows a sign-in page: Sign in with Microsoft Entra ID, and Sign in with a local account if local accounts are allowed. The groups come from the groups claim (SSO_GROUPS_CLAIM; for Entra ID, add the groups claim to the token, or use app roles with SSO_GROUPS_CLAIM=roles), the user name from preferred_username (SSO_USER_CLAIM). Strata checks the ID token's signature against the provider's keys, its issuer, audience, expiry and nonce, and uses PKCE. Account › Sign out ends the session at the provider too.
A sign-in proxy you already run (Authelia, oauth2-proxy, Pomerium, Authentik outpost): SSO_HEADER_USER=Remote-User, SSO_HEADER_GROUPS=Remote-Groups and SSO_HEADER_TRUST, the address the proxy connects to Strata from (127.0.0.1 when it runs on the same machine; several addresses comma-separated, no range: anything in a range could claim to be anyone). The same SSO_ROLE_* apply. The proxy must set the headers itself and drop any coming from the browser. TRUST_PROXY stays what it was, for the client address.
Local accounts stay: for the tests, small installations, and as a way in when the provider is down. LOCAL_LOGIN=admins keeps them for administrators only (break-glass), LOCAL_LOGIN=off turns them off; a refused local sign-in answers like a wrong password.
Names. The history and the proposals name people by the name the provider gives. A name that is also a local account is refused (rename one of them), and an e-mail address is used only once the provider has verified it. Prefer a name people cannot change themselves at the provider (SSO_USER_CLAIM=oid on Entra ID, or sub), or make sure they cannot.
Sessions last SESSION_HOURS (8 by default) with the role given at sign-in. Account › End all single sign-on sessions (admin) signs everyone out, after a group change or when someone leaves.
API tokens for scripts and integrations: Account › API tokens (admin), with a name, a role and a validity of 1 to 730 days. The token (stk_…) is shown once and stored hashed; send it as Authorization: Bearer stk_…. Basic Auth with a local account keeps working for scripts.
Passwords travel with Basic Auth: never expose the app over plain HTTP outside the machine.
.env, set SITE_ADDRESS=strata.example.lan, the name your users will type.docker compose --profile https up -d --build
TRUST_PROXY=private to .env, so that failed sign-ins are counted per user rather than for Caddy as a whole (section 3), and run the command of step 3 again.The Caddyfile uses tls internal: Caddy signs the certificate with its own local authority, which suits an internal network or a VPN. To remove the browser warning, install that authority on your users' machines:
docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt ./caddy-root.crt
If the name is public and reachable from the Internet on ports 80 and 443, remove the tls internal line and Caddy gets a Let's Encrypt certificate by itself.
Point the proxy at http://127.0.0.1:8080. Live updates use Server-Sent Events on /api/events: response buffering must be turned off on that path. nginx example:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /api/events {
proxy_pass http://127.0.0.1:8080;
proxy_buffering off;
proxy_read_timeout 1h;
}
Set TRUST_PROXY=loopback (the proxy on the same machine) so that Strata reads the client address from X-Forwarded-For: failed sign-ins are then limited per client, not for the proxy as a whole. Only trust a proxy that sets this header itself; otherwise anyone could choose the address Strata sees.
The app also works under a sub-path (for example https://intranet/strata/), because it only uses relative paths.
sudo mkdir -p /opt/strata && sudo cp -r strata/* /opt/strata/
cd /opt/strata
sudo npm run setup # npm ci + copies three.js and the fonts into public/vendor
sudo useradd --system --home /opt/strata --shell /usr/sbin/nologin strata
sudo mkdir -p /var/lib/strata && sudo chown strata:strata /var/lib/strata
sudo cp .env.example /etc/strata.env && sudo nano /etc/strata.env && sudo chmod 600 /etc/strata.env
sudo cp deploy/strata.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now strata
journalctl -u strata -f
The service listens on 127.0.0.1:8080. Put an HTTPS reverse proxy in front of it, as in option B.
For a quick test on a workstation:
npm run setup
EDIT_PASS=test npm start
Then open http://localhost:8080.
What the Dockerfile does, step by step, for packagers and other platforms:
| Step | Command | Needs |
|---|---|---|
| Install the dependencies | npm ci | Node.js 20 or later, npm, the npm registry (package-lock.json pins every package: three.js 0.186 and the Archivo and IBM Plex Mono fonts) |
| Copy them into the app | npm run vendor (scripts/vendor.mjs) | writes public/vendor/ |
| Test | npm test | starts the server on 127.0.0.1 |
| Run | node server/server.js | Node.js only: no npm package at run time, nothing downloaded, no write outside DATA_DIR |
The files to ship are package.json, server/, public/ (with public/vendor/) and, for the command line, scripts/users.mjs. PUBLIC_DIR and DATA_DIR say where the app and the data are; each release tag (vX.Y.Z) has a GitHub release with its source archive.
The repository is a flake. To try Strata (data in ./data, listening on 127.0.0.1:8080):
EDIT_PASS=test nix run github:SolidGeeks/strata
On NixOS, the module runs it as a hardened systemd service (its own strata user, data in /var/lib/strata, read-only system, no capabilities, restricted system calls):
{
inputs.strata.url = "github:SolidGeeks/strata";
outputs = { nixpkgs, strata, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
strata.nixosModules.default
{
services.strata = {
enable = true;
settings = { # section 3; HOST defaults to 127.0.0.1, PORT to 8080
EDIT_USER = "admin";
BACKUP_KEEP = 30;
TRUST_PROXY = "loopback"; # behind nginx or Caddy on this host
};
secretFiles.EDIT_PASS = "/run/secrets/strata-admin"; # kept out of the Nix store
# environmentFile = "/run/secrets/strata.env"; # or KEY=value lines
openFirewall = false; # keep it behind an HTTPS reverse proxy (section 5)
};
}
];
};
};
}
Files in secretFiles are read by systemd as root and handed to the service as credentials: they can stay readable by root only. modules = [ ./my-connector ] provides connector modules (MODULES.md); settings.MODULE_REGISTRY = "off" keeps to them. Accounts from the command line: sudo -u strata env DATA_DIR=/var/lib/strata strata-users set alice contributor.
The Nix workflow builds the package (which runs npm test) and runs a NixOS VM test of the module on every change. When package-lock.json changes, npmDepsHash in nix/package.nix has to follow: the build fails and prints the new value. nix flake update moves nixpkgs forward.
To undo a mistake, the simplest is the history in the app (Collaboration › History › Restore). Backups are for losing the server.
Automatic: the server copies db.json into /data/backups/ at startup, every BACKUP_EVERY_HOURS hours, before each import and before each version restore. It keeps the BACKUP_KEEP most recent ones.
Manual (Docker):
./scripts/backup.sh # backups/strata-YYYYMMDD-HHMMSS.tar.gz: map, history, proposals, accounts
./scripts/restore.sh backups/strata-20261024-120000.tar.gz # restores everything
./scripts/restore.sh export.json # restores the map only (db.json or Export JSON)
The restore script backs up what is there before replacing it. After restoring a db.json on its own, history lines newer than that file are ignored.
From the interface, Export JSON downloads the whole map and Import JSON loads it back (on the same instance or another one).
Remember to include the backups folder or the strata-data Docker volume in your usual backups.
Opened without its server (as a local file, or copied to a static web page), the app keeps the map in that browser only: the This browser only badge shows at the top left. To move that map to your instance:
The format is the same: IS, zones, VLANs, assets, contacts, flows and links between IS are taken over as they are. A v0.1 export imports unchanged into v0.2: flow paths are then suggested automatically.
./scripts/backup.sh
# replace the folder's files with the new version (keep your .env)
docker compose up -d --build
The data lives in the Docker volume and is not touched by the rebuild. Before upgrading, read the Upgrading notes of the CHANGELOG for every version you skip.
Trying a release candidate. Before a release, candidates such as 1.0.0-rc.1 are published under their own version only (releases marked Pre-release). To try one, back up, then set its version in docker-compose.yml (image: ghcr.io/solidgeeks/strata:1.0.0-rc.1) and run docker compose pull && docker compose up -d. To go back, set the release you had and restore the backup if the candidate changed the data.
The full reference, with every route's role, request and answer, the data format, the module API and the compatibility policy, is the API reference (docs/api.html).
Every route needs the same credentials as the interface (a local account with Basic Auth, an API token as Authorization: Bearer stk_…, or the session of a signed-in browser), with the rights of the account's role (direct writes need approver). Writes require the headers Content-Type: application/json and X-Requested-With: strata. A script can group several writes into a single history entry with the headers X-Batch: <id> and X-Batch-Label: <URL-encoded label>. To have a tool propose instead of write, give it a contributor account and use POST /api/proposals with {"title", "siteId", "changes": {"si": {...}, "assets": {...}}} (a document set to null = deletion).
| Method and route | What it does |
|---|---|
GET /api/state | The whole map: {si, assets, wan, services, risk, rev, updatedAt} |
GET /api/export | The same content, as a file to download |
PUT /api/si/{id} | Creates or replaces an IS (zones, VLANs, flows, contacts) |
DELETE /api/si/{id} | Deletes an IS (not its assets) |
PUT /api/assets/{id} | Creates or replaces an asset |
DELETE /api/assets/{id} | Deletes an asset |
PUT /api/wan | Replaces the links between IS: {"links": [...]} |
POST /api/import | Replaces the whole map: {si, assets, wan, services, risk} |
GET /api/kpi | The key figures of the dashboard at the end of each period (?points=13&step=7), rebuilt from the history |
GET /api/incidents | Incident mode of every IS: {incidents: {<IS id>: {on, marks, cuts, run, log}}} |
GET /api/muster | Muster's incidents and whether Muster answers ({url, ok, error, incidents}), when MUSTER_URL is set |
GET /api/muster/incidents/{id} | One Muster incident: its frozen playbook, the steps taken, the link to Strata |
POST /api/muster/incidents/{id}/advance, /tasks, /strata | Passed on to Muster (contributor and up): answer a step {edgeId}, tick a task {nodeId, index, done}, report the assets and what was done {site, marks, names, log} |
PUT /api/incidents/{IS id} | Replaces the incident of an IS (contributor and up, no proposal): marks are compromised, suspected or contained |
PUT /api/services | Replaces the common catalogue of Internet services: {"services": [...]} |
PUT /api/risk |
Example: adding an asset from a script (inventory, NetBox, scanner export…):
curl -u admin:PASSWORD -H 'Content-Type: application/json' -H 'X-Requested-With: strata' \
-X PUT https://strata.example.lan/api/assets/srv-erp-01 \
-d '{"site":"lyon","name":"srv-erp-01","type":"server","vlan":"v-lyo-20","ip":"10.20.0.20",
"os":"RHEL 8","role":"ERP","crit":3,
"vulns":[{"id":"CVE-2021-44228","title":"Log4Shell","cvss":10,"sev":"crit"}],
"contacts":[{"name":"Application team","role":"Technical owner","phone":"+33 1 99 00 18 66","email":"[email protected]","dispo":"Business hours"}],
"notes":""}'
The simplest way to learn the exact structure is to Export JSON the demo.
Flows and VLANs are stored in the IS document (si.flows, si.vlans). To add some from a script: read the IS with GET /api/state, change it, then send it back with PUT /api/si/{id}.
Fields added since v0.2 (all optional):
| Field | Where | Content |
|---|---|---|
apps | asset | [{"name":"Apache Tomcat","version":"9.0.54","ports":["tcp/8080","tcp/443"]}] |
via | flow | ids of the devices crossed, in order, for example ["lyo-fw-edge","lyo-fw-int"]. Missing: path suggested automatically. []: direct flow, forced. |
place | VLAN | (v0.4.2) position of the plate on its level, [x, z] from the level's centre. Missing on the whole level: plates lined up from left to right. |
pos | VLAN | position from left to right in its zone (0, 1, 2…). Missing: sorted by VLAN number. |
services | map root, and IS | Internet services: [{"id":"svc-eset","name":"ESET update servers","category":"Antivirus / EDR","hosts":["update.eset.com"],"ports":["tcp/443"],"notes":""}]; in a flow, the source or destination "svc:svc-eset" |
layout | VLAN | (v0.3) positions set by hand in the 2D diagram: {"lyo-app-erp":[3.75,0.5], …}, in plate units from its centre (x to the right, z to the front). Missing: automatic arrangement. |
type: "router" | asset | new type Router / L3 core |
ext | asset, VLAN | (v0.5) ids in the connected tools: {"glpi":"Computer/12","cyberwatch":101,"ipam":"57"} |
src | vulnerability, application, contact | (v0.5) cyberwatch or glpi when the entry comes from a connector (the others count as entered by hand) |
pan | flow | (v0.5) Palo Alto rule(s) behind the flow: {"rules":["in-web"],"scope":"vsys1"} |
business | IS | (v0.6) business services: [{"id":"b-erp","name":"ERP & accounting","crit":3,"owner":"Finance director","notes":"","assets":["lyo-app-erp","lyo-db-erp"]}]. Only the assets the service runs on: dependencies come from the flows. |
epss, kev | vulnerability | (v0.6) EPSS, a probability from 0 to 1, and when the vulnerability is on the CISA list of known exploited vulnerabilities. Used by . |
Strata follows Semantic Versioning. Before 1.0 a minor version may still break things, and the CHANGELOG says how to upgrade. From 1.0 on, every 1.x version keeps working:
/api/ routes, their methods, roles, request and answer fields;Bearer stk_… tokens), the write headers, X-Batch and X-Batch-Label;error key (not its wording);"strataModule": 1, ctx, the SDK) and the store index ("strataRegistry": 1);change, proposals, connectors.Minor versions may add routes, fields and events: ignore what you don't know, and send back the fields you didn't change. Something to be removed is first marked Deprecated in the CHANGELOG, with what to use instead, and keeps working for at least one more minor version. Only a major version removes something without that notice. Security fixes are the exception: a hole is closed in a patch version, and the CHANGELOG says so.
This map describes your weak spots precisely: treat it as sensitive data.
EDIT_PASS.viewer account to people who only need to look.gh attestation verify (section 2).LOCAL_LOGIN=admins and one local admin with a long password (a name your provider does not use), as the way in when the provider is down. Give scripts API tokens with the lowest role and a short validity, not a person's password.429), per account and per client address (LOGIN_MAX_FAILS, LOGIN_MAX_FAILS_PER_ADDRESS, LOGIN_LOCK_MINUTES); browsers already signed in keep working. Behind a reverse proxy, set TRUST_PROXY (section 5).data/connectors.json: protect the volume and the backup archives.MODULE_UPLOAD is off by default and MODULE_REGISTRY=off turns the store off (MODULES.md).| Symptom | Where to look |
|---|---|
| Blank page, nothing in 3D | public/vendor/ is missing: run docker compose up -d --build or npm run setup again, and check access to the npm registry during the build. |
| "This browser only" badge at the top left | The page cannot reach the API: it is opened as a local file, or the proxy blocks /api/. |
| Other users only see changes after a reload | The proxy buffers /api/events (see section 5, option B). |
| "You have read-only access" | You are signed in with the viewer account, or READ_ONLY=true. |
| Switching accounts in the browser | The browser keeps Basic Auth credentials: open a private window or close the browser. |
Permission error on /data (Docker) | Keep the named volume from docker-compose.yml. If you mount a host folder, give it to UID 1000: chown -R 1000:1000 ./data. |
SESSION_HOURS, 8 by default), or at once with End all single sign-on sessions. LDAP is not read directly: use an OpenID Connect provider in front of it (Keycloak, Authentik, Entra ID).apirest.php 9.5/10, phpIPAM 1.4+, PAN-OS 10/11) and tested against simulators, not against your instances: run Test the connection, then a first sync, and review the proposal before publishing it. Fields specific to your version (Cyberwatch path, location rules) can be set without touching the code.any, large range) is flagged instead of drawn./api.php/v2) is not supported yet.npm test
The tests start a real server on a temporary data folder, create one account per role and check the API over HTTP: authentication, roles, protection against cross-site writes, the proposal cycle and the four-eyes rule, conflicts, history and rollback, import (ids, maximum size), and that the server's files cannot be read with ../. Nothing to install: you only need Node.js 20 or later.
The CI workflow runs them on Node 20 and 22 on every push to main and every pull request. A change that touches the API must come with a test.
A pull request must start from the latest release or later: the base job fails when the branch does not include the latest v… tag of main, and says how to update it (rebase or merge main). A few commits behind main only gets a notice.
npm run test:e2e walks through the interface in Chromium with Playwright (tests/e2e.mjs): an admin loads the demo and sees the 3D model and the 2D diagram, a contributor proposes a change, an approver sees it highlighted and approves it. Any JavaScript error in a page fails it. The first time, install the browser with npx playwright install chromium. The CI workflow runs it too (e2e job). A change to the interface must keep it green.
package.json (npm version 0.10.0 --no-git-tag-version), docker-compose.yml, the interface and the docs, and dates its CHANGELOG section.main. The workflow reads the version from package.json and creates the vX.Y.Z tag itself, so there is nothing to type. It waits for a maintainer's approval (the release environment), then runs the tests, publishes the signed image and the GitHub release with its notes.Pushing a vX.Y.Z tag, or publishing a release from the web page with a new tag on main, still works: the tag must then be exactly v and the version (v0.9.0, not V0.9.0 or v.0.9.0).
A version with a pre-release part (1.0.0-rc.1, 0.9.0-beta.2) is published as ghcr.io/solidgeeks/strata:1.0.0-rc.1 only, never as latest, and its GitHub release is marked as a pre-release: docker compose pull keeps the last release.
Contributions follow the Solid standard: sign off every commit with git commit -s (DCO), no CLA. To report a vulnerability, see SECURITY.md.
anyanyInternet services: instead of a single "Internet" cloud, you describe the services allowed (ESET update servers, Ubuntu repositories, Windows Update, NTP, SaaS…) with their FQDNs/IPs and ports. They are shared by every IS (common catalogue, overview) or specific to one IS (+ This IS button). Flows can have a service as their source or destination: they appear next to the cloud in 3D, at the top of the 2D diagram, in architecture plans, and the firewall rules export uses their FQDNs as the destination address.
10.0.0.0/8loopbackprivatePUBLIC_URL, SSO_*, LOCAL_LOGIN, SESSION_HOURS | — | Single sign-on (OpenID Connect or a sign-in proxy) and local accounts beside it: section 4. |
PORT / HOST | 8080 / 0.0.0.0 | Where the server listens. |
DATA_DIR | /data (Docker) | Folder for the data and the backups. |
SITE_ADDRESS | — | DNS name used by the HTTPS proxy (section 5). |
CONNECTORS | on | false turns the connectors off (the built-in ones and the modules). |
MODULE_REGISTRY | https://solidgeeks.github.io/strata/modules/index.json | The module store administrators install from (read only when they open it). off: no store. |
MODULE_UPLOAD | false | true lets administrators install a module package from a file (no checksum: whoever uploads it vouches for it). |
MODULES_DIR | — | A folder of connector modules you provide yourself, one sub-folder each (MODULES.md). |
CONNECTOR_TIMEOUT_MINUTES | 30 | Longest a connector run may take before it is stopped (2 minutes at most for a test). |
MUSTER_URL | — | Address of your Muster server, as this server reaches it (http://muster:8080). Its incidents then open on the map, in the incident sheet of the IS they name. Empty: no connection. |
MUSTER_USER / MUSTER_PASSWORD | strata / — | The Muster account Strata signs in with: create it in Muster with the analyst role. MUSTER_PASSWORD_FILE reads the password from a file. |
MUSTER_POLL_SECONDS | 10 | How often Strata reads Muster's incidents; every page then follows. |
SOURCE_URL | https://github.com/SolidGeeks/strata | The app's Source code link (AGPL): point it at your fork if you modify the code. |
Replaces the risk settings shared by every IS: {"risk": {matrix, baselines, custom, threats}} |
GET /api/history | Latest changes (?before=REV&limit=N); GET /api/history/{rev}: the before/after details |
GET /api/state?at=REV | The map as it was right after version REV |
POST /api/rollback | {"rev": N}: puts the map back to version N (approver) |
GET/POST /api/proposals, GET/PUT/DELETE /api/proposals/{id} | Proposals; POST …/{id}/submit, /withdraw, /comment, /approve ({"force": true} on a conflict), /reject |
GET /api/users, PUT/DELETE /api/users/{name} | Accounts (admin); PUT /api/me/password: {"current","next"} (local accounts) |
GET/POST /api/tokens, DELETE /api/tokens/{id} | API tokens (admin): POST {"name", "role", "days"} answers the token once |
GET /auth/login, /auth/oidc/start, POST /auth/logout | Sign-in page, single sign-on, sign-out ({"location"} to go to) |
GET /api/connectors | Connectors, settings (secrets masked) and latest syncs |
PUT /api/connectors/{id} | A connector's settings (admin); id: cyberwatch, glpi, phpipam, paloalto, or a module's id |
POST /api/connectors/{id}/test | Tests access (approver) |
POST /api/connectors/{id}/run | Runs a sync (approver); {"wait": true} waits for the result. {"file": {"name", "text"}, "overrides": {…}} imports a file, for the modules that do (Palo Alto, CSV inventory: contributor); overrides: the settings the module lets one import change. The older Palo Alto form {"xml", "fileName"} still works |
GET /api/modules | The module store: what the registry offers, and what is installed (admin; ?fresh=1 reads the registry again) |
POST /api/modules/{id}/install | Installs or updates a module from the store (admin) |
POST /api/modules/upload | Installs a package: {"data": "<.tgz in base64>"} (admin, with MODULE_UPLOAD=true) |
DELETE /api/modules/{id} | Removes a module installed from the store, and its settings (admin) |
GET /api/events | SSE stream: event: change with the new revision, proposals, connectors |
GET /healthz | Health check (no authentication) |
trueHISTORY_KEEP changes, 5000 by default); in a map kept in a browser, as far as its daily snapshots.