Publish ITWorx Pulse source
Public source validation / validate (push) Failing after 3m8s

This commit is contained in:
ITWorx Pulse release export
2026-09-03 02:09:19 +02:00
commit bd774932d5
614 changed files with 77116 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
$ErrorActionPreference = 'Stop'
Set-Location (Split-Path -Parent $PSScriptRoot)
foreach ($command in @('go', 'node', 'pnpm', 'python')) {
if (-not (Get-Command $command -ErrorAction SilentlyContinue)) {
throw "Required command not found: $command"
}
}
pnpm install --frozen-lockfile
go test ./...
pnpm test
Write-Host 'Bootstrap verification passed.' -ForegroundColor Green
+16
View File
@@ -0,0 +1,16 @@
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
Set-Location (Split-Path -Parent $PSScriptRoot)
if (-not (Get-Command go -ErrorAction SilentlyContinue)) {
$cachedGo = Join-Path $env:LOCALAPPDATA 'ITWorx-Pulse\toolchains\go1.26.6\bin'
if (-not (Test-Path (Join-Path $cachedGo 'go.exe'))) { throw 'Go 1.26.6 is not available on PATH or in the recorded local toolchain cache.' }
$env:PATH = $cachedGo + ';' + $env:PATH
}
New-Item -ItemType Directory -Path bin -Force | Out-Null
go build -o bin/pulse-api.exe ./cmd/api
go build -o bin/pulse-migrate.exe ./cmd/migrate
go build -o bin/pulse-worker.exe ./cmd/worker
go build -o bin/pulse-agent.exe ./cmd/agent
pnpm --filter @itworx/pulse-web build
+127
View File
@@ -0,0 +1,127 @@
#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import { execFileSync } from "node:child_process";
function fail(message) {
console.error(message);
process.exit(1);
}
function argument(name) {
const index = process.argv.indexOf(name);
return index >= 0 ? process.argv[index + 1] : undefined;
}
const root = fs.realpathSync(argument("--repository") ?? process.cwd());
const outputArgument = argument("--output");
const reportArgument = argument("--report");
const allowlistPath = path.resolve(root, argument("--allowlist") ?? "public-source.allowlist");
if (!outputArgument) fail("Usage: node scripts/export-public-source.mjs --output <new-directory> [--report <file>]");
const output = path.resolve(outputArgument);
const reportPath = reportArgument ? path.resolve(reportArgument) : undefined;
if (output === root || output.startsWith(`${root}${path.sep}`)) fail("The public export must be outside the canonical repository.");
if (fs.existsSync(output)) fail(`Output already exists: ${output}`);
if (!fs.existsSync(path.join(root, "LICENSE"))) fail("Public export blocked: confirm the license proposal and add LICENSE first.");
const status = execFileSync("git", ["-C", root, "status", "--porcelain=v1", "--untracked-files=all"], { encoding: "utf8" });
if (status.trim()) fail("Public export blocked: the canonical repository must be clean and committed.");
const allowlist = fs.readFileSync(allowlistPath, "utf8")
.split(/\r?\n/u)
.map((line) => line.trim())
.filter((line) => line && !line.startsWith("#"));
function selected(relativePath) {
return allowlist.some((rule) => rule.endsWith("/**") ? relativePath.startsWith(rule.slice(0, -2)) : relativePath === rule);
}
const deniedPrefixes = [".agents/", ".claude/", ".codex/", "artifacts/", "design/", "planning/", "prompts/"];
const deniedRootFiles = new Set(["AGENTS.md", "AUDIT.md", "CURRENT_STATE.md", "DECISIONS.md", "MASTER_PROMPT.txt", "PACKAGE_MANIFEST.md", "PACKAGE_REPORT.md", "PLANS.md", "ROADMAP.md", "START_HERE.md"]);
function denied(relativePath) {
return deniedRootFiles.has(relativePath)
|| path.posix.basename(relativePath) === "AGENTS.md"
|| deniedPrefixes.some((prefix) => relativePath.startsWith(prefix));
}
const replacements = [
[/https?:\/\/pulse\.itworx\.tech/giu, "https://pulse.example.com"],
[/\bgitea\.itworx\.tech\b/giu, "git.example.com"],
[/\b192\.168\.10\.150\b/gu, "192.0.2.10"],
[/\bTower\.local\b/gu, "unraid.example.test"],
[/\/mnt\/user\/appdata\/itworx-pulse/gu, "/srv/pulse"],
[/C:\\Users\\Jens\\/gu, "C:\\Users\\example\\"]
];
function renderedBytes(sourceBytes) {
if (sourceBytes.includes(0)) return sourceBytes;
let value = sourceBytes.toString("utf8");
for (const [expression, replacement] of replacements) value = value.replace(expression, replacement);
return Buffer.from(value, "utf8");
}
const privatePatterns = [
{ id: "private-domain", expression: /\b(?:pulse|gitea)\.itworx\.tech\b/iu },
{ id: "private-hostname", expression: /\bTower\.local\b/iu },
{ id: "private-lan-address", expression: /\b192\.168\.10\.150\b/u },
{ id: "operator-appdata-path", expression: /\/mnt\/user\/appdata\/itworx-pulse/iu },
{ id: "personal-windows-path", expression: /[A-Z]:\\Users\\Jens\\/iu },
{ id: "private-repository-owner", expression: /(?:git@[^\s:]+:|https?:\/\/[^\s/]+\/)Jens\/ITWorx-Pulse/iu }
];
const tracked = execFileSync("git", ["-C", root, "ls-files", "-z"], { encoding: "utf8" })
.split("\0").filter(Boolean).map((entry) => entry.replaceAll("\\", "/"));
const files = tracked.filter((relativePath) => selected(relativePath) && !denied(relativePath))
.sort((left, right) => left.localeCompare(right, "en"));
if (files.length === 0) fail("The allowlist selected no tracked files.");
const findings = [];
const rendered = new Map();
for (const relativePath of files) {
const source = path.join(root, ...relativePath.split("/"));
const stat = fs.lstatSync(source);
if (!stat.isFile() || stat.isSymbolicLink()) {
findings.push({ rule: "non-regular-file", path: relativePath });
continue;
}
if (stat.size > 5 * 1024 * 1024) {
findings.push({ rule: "oversized-file", path: relativePath, bytes: stat.size });
continue;
}
const bytes = renderedBytes(fs.readFileSync(source));
rendered.set(relativePath, bytes);
if (!bytes.includes(0)) {
const text = bytes.toString("utf8");
for (const rule of privatePatterns) if (rule.expression.test(text)) findings.push({ rule: rule.id, path: relativePath });
}
}
for (const required of ["README.md", "SECURITY.md", "CONTRIBUTING.md", "LICENSE", "go.mod", "package.json", "deploy/compose.yaml", ".gitea/workflows/public-validation.yml"]) {
if (!files.includes(required)) findings.push({ rule: "missing-required-file", path: required });
}
const sourceRevision = execFileSync("git", ["-C", root, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
const report = { schemaVersion: 1, sourceRevision, selectedFiles: files.length, findings };
if (reportPath) {
fs.mkdirSync(path.dirname(reportPath), { recursive: true });
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
}
if (findings.length > 0) fail(`Public export blocked by ${findings.length} finding(s). See ${reportPath ?? "the scan output"}.`);
fs.mkdirSync(output, { recursive: false });
const manifest = [];
for (const relativePath of files) {
const bytes = rendered.get(relativePath);
const destination = path.join(output, ...relativePath.split("/"));
fs.mkdirSync(path.dirname(destination), { recursive: true });
fs.writeFileSync(destination, bytes);
manifest.push({ path: relativePath, bytes: bytes.length, sha256: crypto.createHash("sha256").update(bytes).digest("hex") });
}
fs.writeFileSync(path.join(output, "PUBLIC_SOURCE_EXPORT.md"), `# Curated public source export\n\nGenerated from private canonical revision \`${sourceRevision}\`.\n\nThis parentless candidate excludes private operational history, evidence, planning, prompts, and machine-local agent configuration.\n`, "utf8");
fs.writeFileSync(path.join(output, "PUBLIC_SOURCE_MANIFEST.json"), `${JSON.stringify({ schemaVersion: 1, sourceRevision, files: manifest }, null, 2)}\n`, "utf8");
console.log(`Exported ${files.length} reviewed files to ${output}`);
+175
View File
@@ -0,0 +1,175 @@
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
Set-Location (Split-Path -Parent $PSScriptRoot)
function Get-FreeLoopbackPort {
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
$listener.Start()
try { return ([System.Net.IPEndPoint]$listener.LocalEndpoint).Port } finally { $listener.Stop() }
}
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { throw 'Docker is required for the isolated integration smoke.' }
$goBin = Join-Path $env:LOCALAPPDATA 'ITWorx-Pulse\toolchains\go1.26.6\bin'
if (-not (Get-Command go -ErrorAction SilentlyContinue) -and (Test-Path (Join-Path $goBin 'go.exe'))) { $env:PATH = $goBin + ';' + $env:PATH }
$project = 'itworx-pulse-smoke-' + $PID
$webPort = Get-FreeLoopbackPort
$apiPort = Get-FreeLoopbackPort
$dbPort = Get-FreeLoopbackPort
$baseURL = "http://127.0.0.1:$webPort"
$compose = @('-p', $project, '-f', 'deploy/compose.yaml', '-f', 'deploy/compose.dev.yaml', '-f', 'deploy/compose.smoke.yaml')
$succeeded = $false
$env:PULSE_POSTGRES_PASSWORD = 'smoke-process-only'
$env:PULSE_DATABASE_URL = 'postgres://pulse:smoke-process-only@pulse-postgres:5432/pulse?sslmode=disable'
$env:PULSE_ENV = 'development'
$env:PULSE_AUTH_MODE = 'mock'
$env:PULSE_SESSION_IDLE_TTL = '30s'
$env:PULSE_SESSION_ABSOLUTE_TTL = '3m'
$env:PULSE_DEV_WEB_PORT = [string]$webPort
$env:PULSE_DEV_API_PORT = [string]$apiPort
$env:PULSE_DEV_DB_PORT = [string]$dbPort
$env:PULSE_SMOKE_WEBHOOK_TOKEN = 'smoke-runtime-only'
try {
Write-Host "== isolated stack ($project) ==" -ForegroundColor Cyan
docker compose @compose config --quiet
docker compose @compose build --quiet
# Migrate and seed before the worker starts. Discovery claims one-minute
# windows, so restarting a worker after an initial empty run is inherently
# timing-dependent and can correctly retain the first claim until the next
# window.
docker compose @compose up -d --wait --wait-timeout 240 pulse-postgres
docker compose @compose run --rm pulse-migrate
Write-Host '== agent snapshot -> discovery -> inventory ==' -ForegroundColor Cyan
@'
INSERT INTO data_sources (id,type,name,configuration_ref)
VALUES ('b1011111-1111-4111-8111-111111111111','unraid','Smoke container agent','smoke/container-agent')
ON CONFLICT (id) DO NOTHING;
INSERT INTO agent_snapshots (agent_id,capability,observed_at,received_at,payload)
VALUES (
'pulse-smoke-agent',
'containers',
now(),
now(),
jsonb_build_object(
'Source', jsonb_build_object('id','pulse-smoke-agent','type','agent'),
'Containers', jsonb_build_array(jsonb_build_object(
'ID','runtime-smoke-1',
'Name','smoke-api',
'Image','itworx/pulse-smoke',
'ImageDigest','sha256:smoke',
'State','running',
'Health','healthy',
'Project','smoke',
'Labels',jsonb_build_object('com.docker.compose.project','smoke','com.docker.compose.service','api')
)),
'ObservedAt', to_jsonb(now()),
'ReceivedAt', to_jsonb(now())
)
)
ON CONFLICT (agent_id,capability) DO UPDATE
SET observed_at=EXCLUDED.observed_at,received_at=EXCLUDED.received_at,payload=EXCLUDED.payload;
INSERT INTO agent_snapshots (agent_id,capability,observed_at,received_at,payload)
VALUES
('pulse-smoke-agent','array',now(),now(),jsonb_build_object(
'source',jsonb_build_object('id','pulse-smoke-agent','type','fixture'),
'state','operational','parity',jsonb_build_object('present',true,'state','idle'),
'members',jsonb_build_array(
jsonb_build_object('id','disk1','name','Disk 1','role','data','state','online','capacityBytes',1000000),
jsonb_build_object('id','parity','name','Parity','role','parity','state','online','capacityBytes',1000000)
),'observedAt',to_jsonb(now()),'receivedAt',to_jsonb(now())
)),
('pulse-smoke-agent','disks',now(),now(),jsonb_build_object(
'source',jsonb_build_object('id','pulse-smoke-agent','type','fixture'),
'disks',jsonb_build_array(jsonb_build_object('id','disk1','name','Disk 1','role','data','state','online','sizeBytes',1000000,'usedBytes',400000)),
'observedAt',to_jsonb(now()),'receivedAt',to_jsonb(now())
)),
('pulse-smoke-agent','pools',now(),now(),jsonb_build_object(
'source',jsonb_build_object('id','pulse-smoke-agent','type','fixture'),
'pools',jsonb_build_array(jsonb_build_object('id','cache','name','Cache','filesystem','btrfs','state','healthy','usableBytes',1000000,'usedBytes',250000)),
'observedAt',to_jsonb(now()),'receivedAt',to_jsonb(now())
)),
('pulse-smoke-agent','shares',now(),now(),jsonb_build_object(
'source',jsonb_build_object('id','pulse-smoke-agent','type','fixture'),
'shares',jsonb_build_array(jsonb_build_object('id','share-media','name','Media','usedBytes',250000,'sizeObservedAt',to_jsonb(now()),'sizeState','cached')),
'observedAt',to_jsonb(now()),'receivedAt',to_jsonb(now())
))
ON CONFLICT (agent_id,capability) DO UPDATE
SET observed_at=EXCLUDED.observed_at,received_at=EXCLUDED.received_at,payload=EXCLUDED.payload;
'@ | docker compose @compose exec -T pulse-postgres psql -v ON_ERROR_STOP=1 -U pulse -d pulse | Out-Null
if ($LASTEXITCODE -ne 0) { throw 'failed to seed bounded container agent snapshot' }
docker compose @compose up -d --wait --wait-timeout 240
$inventoryReady = $false
for ($attempt = 0; $attempt -lt 30; $attempt++) {
Start-Sleep -Seconds 2
$inventoryRows = docker compose @compose exec -T pulse-postgres psql -U pulse -d pulse -Atc "SELECT count(*) FROM entities WHERE canonical_name='smoke/api';"
if ($LASTEXITCODE -eq 0 -and [int]($inventoryRows | Select-Object -Last 1) -eq 1) { $inventoryReady = $true; break }
}
if (-not $inventoryReady) { throw 'worker did not reconcile the fresh agent container snapshot' }
$identityRows = docker compose @compose exec -T pulse-postgres psql -U pulse -d pulse -Atc "SELECT (SELECT count(*) FROM entity_aliases WHERE source_id='b1011111-1111-4111-8111-111111111111' AND external_type='application-project' AND external_id='smoke')::text || ',' || (SELECT count(*) FROM entity_aliases WHERE source_id='b1011111-1111-4111-8111-111111111111' AND external_type='container-service' AND external_id='smoke/api')::text || ',' || (SELECT count(*) FROM container_aliases WHERE source_id='b1011111-1111-4111-8111-111111111111')::text;"
if (($identityRows | Select-Object -Last 1) -ne '1,1,1') { throw "discovery identity is not idempotent: $identityRows" }
$session = [Microsoft.PowerShell.Commands.WebRequestSession]::new()
Invoke-RestMethod -Uri "$baseURL/auth/test-login" -WebSession $session | Out-Null
$rule = @{
schemaVersion = 1
id = 'f1011111-1111-4111-8111-111111111111'
name = 'Integratiesmoke CPU'
enabled = $true
severity = 'critical'
scope = @{ serverId = 'smoke-host' }
condition = @{ inputType = 'metric'; metric = 'host.cpu.utilization'; operator = '>='; threshold = 1; aggregation = 'avg'; windowSeconds = 60 }
evaluationIntervalSeconds = 5
pendingSeconds = 0
resolveSeconds = 0
cooldownSeconds = 60
unknownBehavior = 'become-unknown'
groupBy = @()
suppressWhen = @()
message = @{ titleKey = 'smoke.cpu.title'; bodyKey = 'smoke.cpu.body' }
} | ConvertTo-Json -Depth 8 -Compress
Invoke-RestMethod -Method Post -Uri "$baseURL/api/v1/alert-rules" -WebSession $session -ContentType 'application/json' -Body $rule | Out-Null
Write-Host '== source -> alert -> webhook ==' -ForegroundColor Cyan
$fixtureStatus = $null
for ($attempt = 0; $attempt -lt 45; $attempt++) {
Start-Sleep -Seconds 2
$fixtureJSON = docker compose @compose exec -T pulse-smoke-fixture /pulse-integration-fixture get http://127.0.0.1:9090/smoke/status
if ($LASTEXITCODE -ne 0) { continue }
$fixtureStatus = $fixtureJSON | ConvertFrom-Json
if ($fixtureStatus.prometheusRequests -gt 0 -and $fixtureStatus.events.firing -gt 0) { break }
}
# One custom smoke rule plus the two implementation-owned aggregate metric
# defaults must all reach the approved Prometheus fixture. This guards the
# alert-scope/catalog contract, not merely a single happy-path query.
if ($null -eq $fixtureStatus -or $fixtureStatus.prometheusRequests -lt 3 -or $fixtureStatus.events.firing -lt 1) {
throw "full alert chain did not complete: $($fixtureStatus | ConvertTo-Json -Compress)"
}
$workerLogs = docker compose @compose logs --no-color pulse-worker 2>$null
if ($workerLogs -match 'alert metric query failed') { throw 'implementation-owned alert rule failed its bounded metric query' }
if ($fixtureStatus.uniqueDeliveryKeys -gt $fixtureStatus.webhookRequests) { throw 'receiver observed inconsistent idempotency keys' }
$snapshotRows = docker compose @compose exec -T pulse-postgres psql -U pulse -d pulse -Atc "SELECT count(*) FROM agent_snapshots WHERE agent_id='pulse-smoke-agent' AND capability IN ('host','processes');"
if ([int]($snapshotRows | Select-Object -Last 1) -lt 2) { throw "agent snapshots missing: $snapshotRows" }
$deliveredRows = docker compose @compose exec -T pulse-postgres psql -U pulse -d pulse -Atc "SELECT count(*) FROM notification_outbox WHERE event_type='firing' AND status='delivered';"
if ([int]($deliveredRows | Select-Object -Last 1) -lt 1) { throw "delivered firing audit missing: $deliveredRows" }
Write-Host '== real browser -> web proxy -> API -> PostgreSQL ==' -ForegroundColor Cyan
$env:PULSE_E2E_REAL_BASE_URL = $baseURL
pnpm --filter @itworx/pulse-web exec playwright test tests/e2e/real-stack.spec.ts --project=desktop-chromium
if ($LASTEXITCODE -ne 0) { throw 'real-stack Playwright gate failed' }
Write-Host ("INTEGRATION SMOKE: PASS sourceQueries={0} webhookAttempts={1} uniqueDeliveries={2}" -f $fixtureStatus.prometheusRequests, $fixtureStatus.webhookRequests, $fixtureStatus.uniqueDeliveryKeys) -ForegroundColor Green
$succeeded = $true
} finally {
if (-not $succeeded) {
docker compose @compose ps 2>$null
docker compose @compose logs --no-color --tail 120 2>$null
}
# The project name is unique to this process. `-v` removes only the empty or
# smoke-populated volume created above; no pre-existing Docker object can
# match or be referenced by this compose project.
docker compose @compose down --volumes --remove-orphans 2>$null
Remove-Item Env:PULSE_POSTGRES_PASSWORD,Env:PULSE_DATABASE_URL,Env:PULSE_ENV,Env:PULSE_AUTH_MODE,Env:PULSE_SESSION_IDLE_TTL,Env:PULSE_SESSION_ABSOLUTE_TTL,Env:PULSE_DEV_WEB_PORT,Env:PULSE_DEV_API_PORT,Env:PULSE_DEV_DB_PORT,Env:PULSE_SMOKE_WEBHOOK_TOKEN,Env:PULSE_E2E_REAL_BASE_URL -ErrorAction SilentlyContinue
}
+14
View File
@@ -0,0 +1,14 @@
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
Set-Location (Split-Path -Parent $PSScriptRoot)
if (-not (Get-Command go -ErrorAction SilentlyContinue)) {
$cachedGo = Join-Path $env:LOCALAPPDATA 'ITWorx-Pulse\toolchains\go1.26.6\bin'
if (-not (Test-Path (Join-Path $cachedGo 'go.exe'))) { throw 'Go 1.26.6 is not available on PATH or in the recorded local toolchain cache.' }
$env:PATH = $cachedGo + ';' + $env:PATH
}
$unformatted = gofmt -l cmd internal
if ($unformatted) { throw "Go files need formatting: $($unformatted -join ', ')" }
go vet ./...
pnpm lint
+126
View File
@@ -0,0 +1,126 @@
[CmdletBinding()]
param(
[Parameter()]
[ValidatePattern('^https://')]
[string]$BaseUrl = 'https://pulse.example.com',
[Parameter()]
[ValidatePattern('^http://')]
[string]$HttpUrl = 'http://pulse.example.com',
[Parameter()]
[ValidatePattern('^https://')]
[string]$ExpectedOIDCIssuer = 'https://auth.nuklearrabbit.com/application/o/itworx-pulse',
[Parameter()]
[ValidateRange(1, 30)]
[int]$TimeoutSeconds = 10
)
$ErrorActionPreference = 'Stop'
$base = [uri]$BaseUrl
$plain = [uri]$HttpUrl
$issuer = [uri]$ExpectedOIDCIssuer
if (-not [string]::IsNullOrEmpty($base.UserInfo) -or -not [string]::IsNullOrEmpty($plain.UserInfo) -or -not [string]::IsNullOrEmpty($issuer.UserInfo)) {
throw 'Production smoke URLs may not contain embedded credentials.'
}
function Invoke-NoRedirect([uri]$Uri) {
$handler = [System.Net.Http.HttpClientHandler]::new()
$handler.AllowAutoRedirect = $false
$client = [System.Net.Http.HttpClient]::new($handler)
$client.Timeout = [TimeSpan]::FromSeconds($TimeoutSeconds)
try {
$response = $client.GetAsync($Uri).GetAwaiter().GetResult()
$headers = @{}
foreach ($header in $response.Headers) {
$headers[$header.Key] = $header.Value -join ', '
}
foreach ($header in $response.Content.Headers) {
$headers[$header.Key] = $header.Value -join ', '
}
[pscustomobject]@{
StatusCode = [int]$response.StatusCode
Headers = $headers
Content = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult()
}
} finally {
$client.Dispose()
$handler.Dispose()
}
}
function Invoke-Probe([string]$Path) {
Invoke-NoRedirect ([uri]($BaseUrl.TrimEnd('/') + $Path))
}
function Assert-Status($Response, [int[]]$Expected, [string]$Label) {
if ($Expected -notcontains [int]$Response.StatusCode) {
throw "$Label returned HTTP $($Response.StatusCode); expected $($Expected -join '/')"
}
}
function Get-ResponseText($Response) {
if ($Response.Content -is [byte[]]) {
return [System.Text.Encoding]::UTF8.GetString([byte[]]$Response.Content)
}
return [string]$Response.Content
}
Write-Host '== HTTPS health and security headers ==' -ForegroundColor Cyan
$health = Invoke-Probe '/healthz'
Assert-Status $health @(200) 'HTTPS /healthz'
if ((Get-ResponseText $health).Trim() -ne 'ok' -or $health.Headers.'Content-Type' -notmatch '^text/plain') {
throw 'HTTPS /healthz must return exact text/plain ok content.'
}
foreach ($header in @('Strict-Transport-Security', 'Content-Security-Policy', 'X-Content-Type-Options', 'X-Frame-Options')) {
if ([string]::IsNullOrWhiteSpace([string]$health.Headers.$header)) { throw "HTTPS response is missing $header." }
}
if ([string]$health.Headers.'Strict-Transport-Security' -notmatch 'max-age=\d+') {
throw 'HSTS does not contain a max-age directive.'
}
$ready = Invoke-Probe '/readyz'
Assert-Status $ready @(200) 'HTTPS /readyz'
if ($ready.Headers.'Content-Type' -notmatch '^text/plain' -or (Get-ResponseText $ready) -match '<!doctype html') {
throw 'HTTPS /readyz is not genuine text/plain API readiness.'
}
Write-Host '== HTTP to HTTPS enforcement ==' -ForegroundColor Cyan
$redirect = Invoke-NoRedirect ([uri]($HttpUrl.TrimEnd('/') + '/healthz'))
Assert-Status $redirect @(301, 302, 307, 308) 'HTTP /healthz'
$location = [uri]::new($plain, [string]$redirect.Headers.Location)
if ($location.Scheme -ne 'https' -or $location.Host -ne $base.Host) {
throw "HTTP redirect escapes the production HTTPS host: $location"
}
Write-Host '== public exposure boundary ==' -ForegroundColor Cyan
foreach ($path in @('/metrics', '/debug/pprof/')) {
$response = Invoke-Probe $path
Assert-Status $response @(404) $path
if ((Get-ResponseText $response) -match '<!doctype html') { throw "$path fell through to the SPA." }
}
foreach ($path in @('/api/v1/system/status', '/api/v1/system/metrics', '/api/v1/system/diagnostics')) {
$response = Invoke-Probe $path
Assert-Status $response @(401) $path
}
Write-Host '== OIDC entrypoint ==' -ForegroundColor Cyan
$discovery = Invoke-NoRedirect ([uri]($ExpectedOIDCIssuer.TrimEnd('/') + '/.well-known/openid-configuration'))
Assert-Status $discovery @(200) 'OIDC discovery'
$provider = $discovery.Content | ConvertFrom-Json
if (([string]$provider.issuer).TrimEnd('/') -ne $ExpectedOIDCIssuer.TrimEnd('/')) {
throw "OIDC discovery returned an unexpected issuer: $($provider.issuer)"
}
$authorizationEndpoint = [uri][string]$provider.authorization_endpoint
if ($authorizationEndpoint.Scheme -ne 'https' -or $authorizationEndpoint.Host -ne $issuer.Host) {
throw "OIDC discovery returned an unsafe authorization endpoint: $authorizationEndpoint"
}
$login = Invoke-Probe '/auth/login'
Assert-Status $login @(302, 303, 307) '/auth/login'
$loginLocation = [uri]::new($base, [string]$login.Headers.Location)
if ($loginLocation.Scheme -ne $authorizationEndpoint.Scheme -or $loginLocation.Host -ne $authorizationEndpoint.Host -or $loginLocation.AbsolutePath -ne $authorizationEndpoint.AbsolutePath) {
throw "OIDC login redirect does not target the expected issuer: $loginLocation"
}
Write-Host "PRODUCTION PUBLIC SMOKE: PASS ($BaseUrl)" -ForegroundColor Green
+36
View File
@@ -0,0 +1,36 @@
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
Set-Location (Split-Path -Parent $PSScriptRoot)
if (-not (Get-Command go -ErrorAction SilentlyContinue)) {
$cachedGo = Join-Path $env:LOCALAPPDATA 'ITWorx-Pulse\toolchains\go1.26.6\bin'
if (-not (Test-Path (Join-Path $cachedGo 'go.exe'))) { throw 'Go 1.26.6 is not available on PATH or in the recorded local toolchain cache.' }
$env:PATH = $cachedGo + ';' + $env:PATH
}
function Invoke-Checked([string]$Label, [scriptblock]$Command) {
Write-Host "== $Label ==" -ForegroundColor Cyan
& $Command
if ($LASTEXITCODE -ne 0) { throw "$Label failed with exit code $LASTEXITCODE" }
}
Invoke-Checked 'Go tests' { go test ./... }
Invoke-Checked 'Go vet' { go vet ./... }
Invoke-Checked 'Frontend install' { pnpm install --frozen-lockfile }
Invoke-Checked 'Frontend tests' { pnpm test }
Invoke-Checked 'Frontend typecheck' { pnpm typecheck }
Invoke-Checked 'Frontend lint' { pnpm lint }
Invoke-Checked 'Frontend build' { pnpm build }
Invoke-Checked 'API contract' { python tools/check_api_contract.py }
Invoke-Checked 'Schema contracts' { python tools/validate_contracts.py }
Invoke-Checked 'Production wiring' { python tools/check_wiring.py }
Invoke-Checked 'Secret markers' { python tools/check_secrets.py }
Invoke-Checked 'Image digest policy' { bash deploy/verify-image-digests.sh }
if (Test-Path -LiteralPath 'PUBLIC_SOURCE_MANIFEST.json') {
Invoke-Checked 'Public source manifest' { node scripts/validate-public-source.mjs }
} else {
Write-Host 'Public source manifest check skipped in canonical private checkout.' -ForegroundColor Yellow
}
Write-Host 'PUBLIC VERIFY: PASS' -ForegroundColor Green
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env sh
set -eu
# Keep the scanner independent of GitHub-specific composite-action setup.
# The release is immutable and the embedded digest is from the official
# v0.74.0 checksum manifest.
version="0.74.0"
archive="trivy_${version}_Linux-64bit.tar.gz"
expected_sha256="2ae6fe3ee734b7fdf11335663e18c75ea12dccc76062f09f164a3b0f8be4371a"
work_root="${RUNNER_TEMP:-${TMPDIR:-/tmp}}"
work_dir="$(mktemp -d "${work_root%/}/pulse-trivy-${version}.XXXXXX")"
scan_root="${1:-.}"
case "${scan_root}" in
-*) echo "scan root must be a directory path, not an option" >&2; exit 2 ;;
esac
[ -d "${scan_root}" ] || { echo "scan root does not exist: ${scan_root}" >&2; exit 2; }
cleanup() {
rm -rf -- "${work_dir}"
}
trap cleanup EXIT HUP INT TERM
mkdir -p "${work_dir}"
curl --fail --silent --show-error --location \
--output "${work_dir}/${archive}" \
"https://github.com/aquasecurity/trivy/releases/download/v${version}/${archive}"
printf '%s %s\n' "${expected_sha256}" "${work_dir}/${archive}" | sha256sum --check --status
tar -xzf "${work_dir}/${archive}" -C "${work_dir}" trivy
"${work_dir}/trivy" fs \
--scanners vuln \
--include-dev-deps \
--format table \
--severity HIGH,CRITICAL \
--exit-code 1 \
--ignore-unfixed \
--no-progress \
"${scan_root}"
+14
View File
@@ -0,0 +1,14 @@
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
Set-Location (Split-Path -Parent $PSScriptRoot)
if (-not (Get-Command go -ErrorAction SilentlyContinue)) {
$cachedGo = Join-Path $env:LOCALAPPDATA 'ITWorx-Pulse\toolchains\go1.26.6\bin'
if (-not (Test-Path (Join-Path $cachedGo 'go.exe'))) { throw 'Go 1.26.6 is not available on PATH or in the recorded local toolchain cache.' }
$env:PATH = $cachedGo + ';' + $env:PATH
}
python -m unittest discover -s tools/tests -p 'test_*.py' -v
go test ./...
pnpm test
python tools/validate_contracts.py
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
const root = process.cwd();
const required = ["README.md", "SECURITY.md", "CONTRIBUTING.md", "LICENSE", "go.mod", "package.json", "deploy/compose.yaml", ".gitea/workflows/public-validation.yml", "PUBLIC_SOURCE_EXPORT.md", "PUBLIC_SOURCE_MANIFEST.json"];
const missing = required.filter((entry) => !fs.existsSync(path.join(root, entry)));
if (missing.length) {
console.error(`Missing public source files: ${missing.join(", ")}`);
process.exit(1);
}
for (const privatePath of [".agents", ".codex", "artifacts", "design", "planning", "prompts"]) {
if (fs.existsSync(path.join(root, privatePath))) {
console.error(`Private-only path present in public export: ${privatePath}`);
process.exit(1);
}
}
const manifest = JSON.parse(fs.readFileSync(path.join(root, "PUBLIC_SOURCE_MANIFEST.json"), "utf8"));
if (!Array.isArray(manifest.files) || manifest.files.length === 0) {
console.error("PUBLIC_SOURCE_MANIFEST.json contains no files.");
process.exit(1);
}
for (const entry of manifest.files) {
const target = path.join(root, ...entry.path.split("/"));
if (!fs.existsSync(target)) {
console.error(`Manifest file is missing: ${entry.path}`);
process.exit(1);
}
const bytes = fs.readFileSync(target);
const digest = crypto.createHash("sha256").update(bytes).digest("hex");
if (digest !== entry.sha256 || bytes.length !== entry.bytes) {
console.error(`Manifest mismatch: ${entry.path}`);
process.exit(1);
}
}
console.log(`Public source structure and manifest passed (${manifest.files.length} allowlisted files).`);
+71
View File
@@ -0,0 +1,71 @@
param(
[ValidateRange(0.01, 168)] [double]$DurationHours = 24,
[ValidateRange(5, 3600)] [int]$SampleSeconds = 60,
[ValidateRange(30, 86400)] [int]$ReconnectSeconds = 3600,
[string]$OutputDirectory = 'artifacts/evidence/M10-14/raw'
)
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
Set-Location (Split-Path -Parent $PSScriptRoot)
function Get-FreeLoopbackPort {
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
$listener.Start()
try { return ([System.Net.IPEndPoint]$listener.LocalEndpoint).Port } finally { $listener.Stop() }
}
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { throw 'Docker is required for the isolated wallboard soak.' }
if (-not (Get-Command node -ErrorAction SilentlyContinue)) { throw 'Node.js is required for the wallboard soak runner.' }
$goBin = Join-Path $env:LOCALAPPDATA 'ITWorx-Pulse\toolchains\go1.26.6\bin'
if (-not (Get-Command go -ErrorAction SilentlyContinue) -and (Test-Path (Join-Path $goBin 'go.exe'))) { $env:PATH = $goBin + ';' + $env:PATH }
$project = 'itworx-pulse-soak-' + $PID
$webPort = Get-FreeLoopbackPort
$apiPort = Get-FreeLoopbackPort
$dbPort = Get-FreeLoopbackPort
$baseURL = "http://127.0.0.1:$webPort"
$compose = @('-p', $project, '-f', 'deploy/compose.yaml', '-f', 'deploy/compose.dev.yaml', '-f', 'deploy/compose.smoke.yaml')
$resolvedOutput = [System.IO.Path]::GetFullPath((Join-Path (Get-Location) $OutputDirectory))
if (Test-Path -LiteralPath $resolvedOutput) {
$existingArtifact = Get-ChildItem -LiteralPath $resolvedOutput -Force | Select-Object -First 1
if ($existingArtifact) { throw "Refusing to mix soak evidence in non-empty output directory: $resolvedOutput" }
}
New-Item -ItemType Directory -Force -Path $resolvedOutput | Out-Null
$sourceCommit = (git rev-parse HEAD).Trim()
$sourceDirty = [bool](git status --porcelain)
$env:PULSE_POSTGRES_PASSWORD = 'soak-process-only'
$env:PULSE_DATABASE_URL = 'postgres://pulse:soak-process-only@pulse-postgres:5432/pulse?sslmode=disable'
$env:PULSE_ENV = 'development'
$env:PULSE_AUTH_MODE = 'mock'
$env:PULSE_DEV_WEB_PORT = [string]$webPort
$env:PULSE_DEV_API_PORT = [string]$apiPort
$env:PULSE_DEV_DB_PORT = [string]$dbPort
$env:PULSE_SMOKE_WEBHOOK_TOKEN = 'soak-runtime-only'
$env:PULSE_SOAK_BASE_URL = $baseURL
$env:PULSE_SOAK_OUTPUT_DIR = $resolvedOutput
$env:PULSE_SOAK_DURATION_HOURS = [string]$DurationHours
$env:PULSE_SOAK_SAMPLE_SECONDS = [string]$SampleSeconds
$env:PULSE_SOAK_RECONNECT_SECONDS = [string]$ReconnectSeconds
try {
Write-Host "== isolated 24-hour wallboard deployment ($project) ==" -ForegroundColor Cyan
docker compose @compose config --quiet
docker compose @compose build --quiet
docker compose @compose up -d --wait --wait-timeout 240
docker compose @compose images --format json | Set-Content -Path (Join-Path $resolvedOutput 'launch-images.jsonl') -Encoding utf8
@{ project = $project; baseURL = $baseURL; startedAt = (Get-Date).ToUniversalTime().ToString('o'); durationHours = $DurationHours; sampleSeconds = $SampleSeconds; reconnectSeconds = $ReconnectSeconds; sourceCommit = $sourceCommit; sourceDirty = $sourceDirty } | ConvertTo-Json | Set-Content -Path (Join-Path $resolvedOutput 'stack.json') -Encoding utf8
node tools/wallboard-soak.mjs
if ($LASTEXITCODE -ne 0) { throw 'wallboard soak runner failed' }
docker compose @compose ps --format json | Set-Content -Path (Join-Path $resolvedOutput 'final-compose-ps.jsonl') -Encoding utf8
docker compose @compose logs --no-color --timestamps | Set-Content -Path (Join-Path $resolvedOutput 'final-compose.log') -Encoding utf8
Write-Host 'WALLBOARD SOAK: PASS' -ForegroundColor Green
} finally {
if (-not (Test-Path (Join-Path $resolvedOutput 'final-compose.log'))) {
docker compose @compose ps --format json 2>$null | Set-Content -Path (Join-Path $resolvedOutput 'failure-compose-ps.jsonl') -Encoding utf8
docker compose @compose logs --no-color --timestamps 2>$null | Set-Content -Path (Join-Path $resolvedOutput 'failure-compose.log') -Encoding utf8
}
docker compose @compose down --volumes --remove-orphans 2>$null
Remove-Item Env:PULSE_POSTGRES_PASSWORD,Env:PULSE_DATABASE_URL,Env:PULSE_ENV,Env:PULSE_AUTH_MODE,Env:PULSE_DEV_WEB_PORT,Env:PULSE_DEV_API_PORT,Env:PULSE_DEV_DB_PORT,Env:PULSE_SMOKE_WEBHOOK_TOKEN,Env:PULSE_SOAK_BASE_URL,Env:PULSE_SOAK_OUTPUT_DIR,Env:PULSE_SOAK_DURATION_HOURS,Env:PULSE_SOAK_SAMPLE_SECONDS,Env:PULSE_SOAK_RECONNECT_SECONDS -ErrorAction SilentlyContinue
}