Changelog
Append-only record of operational changes. Bump OPERATIONS_DOC_VERSIONand add an entry whenever install, configure, operate, results-reading or security posture content changes. Exports are timestamped.
History
| Date | Version | Summary |
|---|---|---|
| 2026-10-04 | 2.15.0 | CMMC dashboard (staff) at /settings/cmmc, renamed from 'CMMC customers'. Overview tiles: customers, compliant, in progress, expired, renewing within 30 days, device checks pending (never started, script issued but never run, any device failing, or last check older than 30 days). Tiles filter the list; list sorted by CAGE code with search by CAGE or company. listCmmcProfilesStaff now also returns device_link_issued (boolean only, never the token hash), device_count and failing_devices (latest run per hostname). No schema change. |
| 2026-10-04 | 2.14.0 | CMMC controls CSV export on /cmmc ('Export CSV' button in the page header). One row per FAR 52.204-21 control: code, title, status (implemented / not_implemented), missing attestation items, failing device checks, plus company, CAGE code, SPRS status, renewal due date and latest device-check date repeated per row. Built client-side from data already loaded via src/lib/export-download.ts (downloadCsv); no new server fn, no schema change. Filename cmmc-controls-YYYYMMDD-HHMMSS.csv. |
| 2026-10-04 | 2.13.0 | CMMC evidence review on /cmmc ('Evidence review' tab). Customers upload up to 8 policy/evidence files (PDF sent inline as input_file; TXT/Markdown as text; ~8 MB total) plus notes; openai/gpt-6-astra via the Lovable AI Gateway Responses API (streamed, store:false) maps them to all 15 FAR 52.204-21 controls (Covered / Partial / Missing with cited file names), flags missing documentation and lists prioritized next steps. Server fn reviewCmmcEvidence in src/lib/api/cmmc-advisor.functions.ts (requireSupabaseAuth; profile read through tenant RLS). Files and results are not stored. Word files must be saved as PDF first. |
| 2026-10-04 | 2.12.0 | Signed CMMC device-check scripts. issueCmmcDeviceToken now generates the Windows and macOS scripts server-side and signs them with NSO's Ed25519 key (derived from the CMMC_SCRIPT_SIGNING_SEED secret); the last line of each script is '# NSO-SIGNATURE v1 ed25519 <base64>' covering the bytes before it. NSO never signs browser-supplied content. Public key at /api/public/cmmc/signing-key; customers verify a file in their browser at the public /verify-script page (file is not uploaded; CRLF line endings tolerated) or compare the SHA-256 fingerprint shown on /cmmc with Get-FileHash / shasum. Microsoft Authenticode signing (removes the SmartScreen warning) is pending a purchased code-signing certificate. |
| 2026-10-04 | 2.11.0 | CMMC device-check advisor on /cmmc (Device check tab, 'Explain my results'). Customers submit device-check results — the latest run per computer is included automatically (read through tenant RLS), plus optional pasted script output and notes — and openai/gpt-6-astra via the Lovable AI Gateway Responses API (streamed, store:false) explains each risk and returns prioritized fixes with re-check steps. Server fn explainDeviceCheck in src/lib/api/cmmc-advisor.functions.ts (requireSupabaseAuth). Answers are not stored. 429/402 shown as friendly errors. |
| 2026-10-04 | 2.10.0 | CMMC Level 1 FastTrack (NSO side of the Bid Board CMMC upgrade). Customer flow at /cmmc: company & scope (CAGE code, bidding users, M365/Google), account-hardening checklist, read-only device scripts (audit-cmmc-l1.ps1 / .sh) posting to /api/public/compliance/verify with a per-profile bearer token (hash stored only, 30 runs/hour limit), office & process attestations, generated SSP / Acceptable Use Policy / Media Sanitization Protocol, and a guided SPRS walkthrough — the company officer files in SPRS and confirms; NSO never files. All 15 FAR 52.204-21 controls scored pass/fail with no partial credit. New tables compliance_profiles, endpoint_audit_runs, compliance_control_attestations, compliance_handoffs (tenant-scoped RLS). Mesh: bidboard may call cmmc_handoff (returns a one-time 30-minute /cmmc-start link) and cmmc_status (lookup by CAGE). NSO sends cmmc.compliant / cmmc.renewal_due / cmmc.expired to bidboard and mgr; delivery failures go to mgr via reportIssueToMgr. Daily renewal sweep at /api/public/hooks/cmmc-renewals (cron secret): reminder 30 days before due (day 335), expiry at day 365. Staff list at /settings/cmmc. Billing stays with Bid Board. |
| 2026-09-22 | 2.9.0 | Ask operations at /settings/mesh-ask (staff-only, Operations nav). Operators ask a plain-language question about mesh health checks and the operations log over a 24-hour, 7-day or 30-day window; the server assembles an evidence pack from public.svc_outbound_events and public.svc_inbound_events (per-peer attempts/successes/failures, success rate, avg and p95 duration, per-day buckets, inbound counts, recent failures) plus the most recent OPERATIONS_CHANGELOG entries, and asks openai/gpt-6-astra via the Lovable AI Gateway Responses API (streamed, store:false) for an answer with Answer / Relevant findings / Trends / Suggested next steps / Caveats. Server fns in src/lib/api/mesh-ask.functions.ts — askMeshOperations and listMeshAskAnswers, both gated by requireSupabaseAuth + assertStaff. New table public.mesh_ask_answers (question, window_days, evidence, markdown, model, duration_ms) with staff-only read via public.is_staff(auth.uid()) and service_role writes. Secret values are never included in the evidence pack — only presence booleans. |
| 2026-09-22 | 2.8.1 | Timestamped exports. /operations/changelog offers JSON and CSV downloads of the operations log (JSON also carries OPERATIONS_DOC_VERSION and lastUpdated). /settings/mesh-health offers JSON and CSV downloads of the current mesh check rollup — per-peer attempts/successes/failures, success rate, avg and p95 duration, inbound count, bidirectional flag and last attempt/success/failure details; the JSON export also embeds totals, recent failures and the operations log for a single hand-off artifact. Exports are built client-side from data already loaded (src/lib/export-download.ts); no new server fn, no schema change, no secret values included. Filenames are nso-operations-log-YYYYMMDD-HHMMSS and nso-mesh-check-results-YYYYMMDD-HHMMSS. |
| 2026-09-22 | 2.8.0 | Mesh health history dashboard at /settings/mesh-health (super-admin only, Admin nav). Rolls up public.svc_outbound_events and public.svc_inbound_events per peer over a 24-hour, 7-day or 30-day window: outbound attempts/successes/failures, success rate, average and p95 duration, last outbound/inbound timestamps, a bidirectional flag, per-day sparkline buckets and a recent-failure list. Server fns in src/lib/api/mesh-health.functions.ts — getMeshHealthHistory (read-only rollup) and runMeshHealthSweep (signed ping to every configured peer, each result persisted to svc_outbound_events with flow=ping, title 'Mesh health sweep'). No schema change; both fns are gated by requireSupabaseAuth + assertSuperAdmin and never expose secret values. |
| 2026-09-22 | 2.7.0 | Service mesh: added grants as a peer (ping, health_check inbound allowlisted) with SVC_PEER_URL_GRANTS pointing at its alternate transport path https://ntigrants.com/api/mesh/dispatch. callPeer() and the cross-peer probe now treat any SVC_PEER_URL_<PEER> that already carries a path as a complete transport URL — the signed logical path /api/public/svc/dispatch is appended only for bare origins. Signing is always over the canonical logical path regardless of transport path. Grants' receiver was relaxed to accept either signed address; full cross-peer probe now 28/28 green (mgr, bidboard, jackiepoole, hire, ntiwriting, runtowork, grants — all eight systems both directions). |
| 2026-09-19 | 2.6.0 | Mesh client console at /settings/mesh (super-admin only, linked from the Admin nav). Sends a signed ping, health_check, or event_report to any configured peer via callPeer(), with an optional JSON payload, and shows peer readiness from SVC_SECRET_TO_/SVC_SECRET_FROM_/SVC_PEER_URL_ presence without exposing values. Every attempt is persisted to the new public.svc_outbound_events table (peer, flow, event type, severity, ok, HTTP status, request/event id, error, response, duration) — staff-only SELECT via public.is_staff(auth.uid()), no client insert/update/delete. The page shows a live auto-refreshing log of outbound deliveries alongside inbound events from public.svc_inbound_events. |
| 2026-09-19 | 2.5.1 | Service mesh: added runtowork as a peer (ping, health_check inbound allowlisted). callPeer() and the cross-peer probe now accept either a bare origin or a full dispatch URL in SVC_PEER_URL_<PEER> — the signed logical path /api/public/svc/dispatch is appended only when not already present, so full transport URLs (hire's Edge Function URL, runtowork's full dispatch address) never double the path. Full cross-peer probe: 24/24 green (mgr, bidboard, jackiepoole, hire, ntiwriting, runtowork). |
| 2026-09-09 | 2.5.0 | Enterprise Segmentation Standard v1 applied to nso. Added public.is_staff_email() and public.is_staff() (approved corporate email domain AND a platform_user_roles row), executable by service_role only. Added src/lib/access.server.ts as the single server-side decision point: STAFF_DOMAINS, isStaffEmail(), assertStaff(), assertRole(), assertTenant(). Every outbound mgr event now carries payload.brand = "nso" so mgr can group its issues dashboard by brand. Confirmation event segmentation.standard.applied (brand nso, version v1) delivered to mgr. |
| 2026-08-28 | 2.4.1 | Service mesh: updated nso's SVC_PEER_URL_HIRE to hire's Supabase Edge Function transport URL (https://yxjkfzrnhychklrezvyy.supabase.co/functions/v1/svc-dispatch). callPeer() appends the signed logical path /api/public/svc/dispatch and hire's function accepts the combined path. hire is no longer blocked; bidboard and jackiepoole remain pending their side of the handoff. Updated /api-docs troubleshooting with hire's direct transport URL, the no-secret probe curl, and the signed-ping example. |
| 2026-08-11 | 2.4.0 | Mesh idempotency: POST /api/public/svc/dispatch now accepts an x-svc-request-id idempotency key. Mutating flows (event_report) run at most once per (caller, request id) — retries replay the recorded response with replayed:true, a key reused with a different payload returns 409 idempotency_key_reuse, and a concurrent in-flight attempt returns 409 request_in_progress (retryable, stale claims expire after 60s). Outbound callPeer() generates one request id per logical call and reuses it across its automatic retries. |
| 2026-08-07 | 2.3.2 | Turnstile: re-enabled VITE_TURNSTILE_SITE_KEY (hostnames must be added in the Cloudflare dashboard before publish). Added docs/deploy/nso-deployment-runbook.md — operator runbooks for standing up the OpenVAS/GVM stack and enrolling+running a scanner worker. Smoke/regression suite verified green (svc-mesh, tenant-create, secret-column-grants, webhook-secret-access). |
| 2026-08-04 | 2.3.1 | Mesh: added POST /api/public/svc/relay — a bearer-authenticated (AGENT_RELAY_SECRET) endpoint that forwards an ad-hoc notice to mgr via reportEventToMgr(), so maintainers and automation can push information to mgr without signing into nso. |
| 2026-08-04 | 2.3.0 | Observability: nso now reports its own issues to mgr automatically via reportIssueToMgr() — unhandled server errors (system.error), asset verification failures (asset.verification_failed), and critical scan findings (finding.critical), with 5-minute de-duplication. Operators monitor issues in mgr instead of logging into nso. |
| 2026-07-13 | 2.2.0 | Documentation: added a step-by-step 'Update the service role key in env vars and redeploy' section to /operations/configure and README-SUPABASE.md, covering post-rotation propagation on Lovable Cloud (server secrets auto-updated), local .env.local sync, redeploy trigger, and verification. |
| 2026-07-13 | 2.1.9 | Documentation: added a step-by-step 'Rotate the Supabase service role key (Lovable Cloud)' guide to /operations/configure and README-SUPABASE.md covering when to rotate, the Cloud admin rotation flow, verification steps, and Lovable Cloud limitations (no Supabase dashboard access, no manual .env edits, no post-rotation recovery). |
| 2026-07-12 | 2.1.8 | Documentation: added a 'Where to paste API keys and env vars' section to /guides/supabase and README-SUPABASE.md with a step-by-step Supabase Project Settings → API walkthrough, a paste-destination table, and local vs. deployed secret-store guidance. |
| 2026-07-11 | 2.1.7 | Documentation: added an interactive client-side environment-variable validator on /guides/supabase that checks required names, HTTPS URL format, and JWT-shaped Supabase key format in real time. |
| 2026-07-11 | 2.1.6 | Documentation: added copy-to-clipboard buttons for API key / env var names and every code snippet on /guides/supabase to improve setup ergonomics. |
| 2026-07-11 | 2.1.5 | Documentation: added public /guides/supabase page mirroring README-SUPABASE.md with prerequisites, env vars, browser insert example, and HMAC-signed ingest webhook instructions. |
| 2026-07-11 | 2.1.4 | Documentation: added README-SUPABASE.md with prerequisites, installation steps, required environment variables, and copy-paste examples for inserting data and calling the HMAC-signed ingest webhook. |
| 2026-07-11 | 2.1.3 | CI quality gates: added a production build workflow (.github/workflows/build.yml) that runs `bun run build` on every pull request and main branch push, ensuring the production bundle compiles cleanly before merge. |
| 2026-07-11 | 2.1.2 | CI quality gates: added a Prettier format check step to the lint workflow so formatting regressions fail builds, and added the `format:check` package script. |
| 2026-07-11 | 2.1.1 | CI quality gates: added an ESLint lint workflow (.github/workflows/lint.yml) that runs on every pull request and main branch push, and fixed all existing lint errors so the check passes cleanly alongside the typecheck workflow. |
| 2026-07-11 | 2.1.0 | Dashboard polish + Compliance workspace. Severity chart Y-axis now shows real counts (or a % toggle) instead of a fixed 0–1 scale, and a System Health banner surfaces blocking (no verified assets) and warning (no scans in 30d) states. Compliance page is now framework-scoped (SOC 2, GDPR, ISO 27001, NIST CSF, CIS v8) via URL-persisted tabs, and each control has a detail page listing mapped vulnerabilities plus a private, tenant-scoped evidence upload flow (compliance-evidence storage bucket, signed downloads). |
| 2026-07-02 | 2.0.0 | Redesign v2: sidebar regrouped into Monitor / Governance / Operations / Admin / Docs sections with per-item microcopy tooltips. Dashboard posture score now hides behind a 'Pending' state until at least one verified asset has been scanned; severity chart shows an empty-state CTA when there are zero findings; recent-scan-activity panel is replaced by an onboarding checklist when no scans exist. Sign-out moved from a red bottom-of-sidebar button into a neutral Account dropdown menu. |
| 2026-06-26 | 1.9.6 | Platform super-admins can now bypass DNS verification on a per-asset basis (with a required typed reason) so a one-off scan can run without waiting for live DNS propagation. Bypassed assets show a distinct amber 'Bypassed by admin' badge — never the green 'Verified' badge — and every flip is double-logged to /settings/platform-audit and /settings/verification-audit. |
| 2026-06-26 | 1.9.5 | Asset verification now separates basic DNS resolution from TXT ownership verification: Retry/Refresh checks A, AAAA, and CNAME records and shows when a domain resolves even if the required ownership TXT token is still pending. |
| 2026-06-26 | 1.9.4 | Individual asset Retry verification now treats DNS mismatches/no-record responses as pending status updates instead of red failure toasts; red toasts are reserved for the check itself failing to run. |
| 2026-06-26 | 1.9.3 | Asset DNS refresh now treats unresolved/mismatched TXT checks as pending status updates instead of emitting red failure toasts for every auto-refresh or Refresh all run; individual Retry verification still shows the detailed mismatch reason. |
| 2026-06-26 | 1.9.2 | Asset DNS verification now shows the exact required TXT value beside both accepted host names, with a one-click copy button and a note that unrelated existing TXT records such as SPF or Google verification do not satisfy ownership verification. |
| 2026-06-26 | 1.9.1 | Asset DNS verification UI now displays both TXT lookup candidates — the asset's root host (e.g., sun.com) and the _nso-verify subdomain — and clearly labels which candidate matched, mismatched, or returned no record. |
| 2026-06-26 | 1.9.0 | Asset DNS verification now accepts a TXT record on either the asset's root host (e.g., sun.com) or the _nso-verify subdomain. The app checks both hosts in parallel and reports the result for each. |
| 2026-06-25 | 1.8.0 | API docs: documented the cron hook `/api/public/hooks/scan-schedules-tick` with the required `Authorization: Bearer $CRON_SECRET` (or `x-cron-secret`) header and added production and local curl examples to the /api-docs page. |
| 2026-06-25 | 1.7.0 | B4 guardrails + ZAP chunking. New tenant settings `daily_scan_quota` (default 200) and `per_asset_concurrent_scans` (default 1) enforced at queue time, at chunked-queue time, and again at worker lease time so a busy asset can't starve workers. Scans page shows a live 'X of Y scans used today / N remaining' banner. ZAP is now chunkable: 'Queue chunked scan' splits a default URL-path seed list (/, /api, /admin, /login, /static, /v1, /v2, /health) across N parallel children; chunk 0 also runs the passive baseline. Worker zap runner honors chunk_spec.paths and chunk_spec.profile. |
| 2026-06-25 | 1.6.0 | Scan chunking (Track B3) for Nmap + Nuclei: Scans page now offers 'Queue chunked scan' (2–8 parallel chunks). Nmap chunks split the 1–65535 port range; Nuclei chunks split by severity bucket. Children run independently on any available worker; a new DB trigger aggregates child status/percent into the parent (chunking → aggregating → succeeded/failed). Worker SDK honors chunk_spec via -p / -severity / -tags flags. |
| 2026-06-25 | 1.5.0 | Desktop workers (Track B1+B2): cross-platform nso-worker binaries (macOS/Linux/Windows × amd64/arm64) released via GitHub Actions. New 'Enroll desktop worker' flow on Settings → Workers issues a one-time 8-char code + QR; `nso-worker enroll --api-url <URL> --code <CODE>` exchanges it for a bearer and saves config to ~/.nso/worker.json. Public endpoint POST /api/public/worker/v1/enroll backs the flow. Codes expire in 15 minutes and are single-use. |
| 2026-06-25 | 1.4.0 | External report storage (Track A): scan workers can now upload raw reports to a tenant-owned S3 / R2 / MinIO bucket instead of the built-in storage. New Settings → Storage page lets owners add, test, and default an S3-compatible sink. Two-phase worker upload (POST /report/init → PUT signed URL → POST /report/finalize) added alongside the existing single-shot /report route for backward compatibility. |
| 2026-06-25 | 1.3.0 | Analyze page: saved history panel. Signed-in users automatically save each uploaded result + AI explanation to a private, RLS-scoped history (new public.result_analyses table); view or delete past entries from the page. |
| 2026-06-25 | 1.2.0 | New /operations/analyze page: upload a JSON/CSV/XML/log result file and get an AI-generated plain-language explanation plus likely failure causes (via Lovable AI gateway, server-side). |
| 2026-06-25 | 1.1.0 | Reading results: added step-by-step walkthrough, example outputs per scanner (Edge/Nmap/Nuclei/ZAP/Trivy), and common failure modes (DNS not verified, worker offline, timeouts, FP patterns). |
| 2026-06-25 | 1.0.0 | Initial release: /services catalog, /operations living document (install, configure, operate, how to read results, security posture, dashboard, changelog). |