Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
175 commits
Select commit Hold shift + click to select a range
e64f3df
feat(center): add center-scoped safety workspace
sultanasiri001-sys Oct 2, 2026
f544be3
feat(center): load center safety workspace
sultanasiri001-sys Oct 2, 2026
07ae192
feat(center): route safety to center-owned trip records
sultanasiri001-sys Oct 2, 2026
8709e28
security: override transitive node-forge above vulnerable 1.4.0
sultanasiri001-sys Oct 2, 2026
79088b8
security: remove ineffective forge override pending upstream fix
sultanasiri001-sys Oct 2, 2026
a895c23
security: quarantine known Expo forge advisory without weakening othe…
sultanasiri001-sys Oct 2, 2026
91f5eeb
security: remove ineffective forge override pending upstream fix
sultanasiri001-sys Oct 2, 2026
3189845
security: quarantine known Expo forge advisory without weakening othe…
sultanasiri001-sys Oct 2, 2026
feefd3b
feat(center): add center-scoped safety workspace
sultanasiri001-sys Oct 2, 2026
1a6c7c0
feat(center): load center safety workspace
sultanasiri001-sys Oct 2, 2026
2ffba95
feat(center): route safety to center-owned trip records
sultanasiri001-sys Oct 2, 2026
3a182a1
Restore fail-closed dependency gate without package-name exceptions
sultanasiri001-sys Oct 2, 2026
c27c2e8
Capture a short-lived tracked-source snapshot for the resumed project…
sultanasiri001-sys Oct 2, 2026
a326710
Apply hash-guarded review repairs on PR 409 branch only
sultanasiri001-sys Oct 2, 2026
4832008
Fix overlapping profile loads and center safety routes; remove tempor…
github-actions[bot] Oct 2, 2026
c986557
Render center safety using the server contract and fence stale sessio…
sultanasiri001-sys Oct 2, 2026
9f10f29
Add browser regressions for center safety routes, schema and stale re…
sultanasiri001-sys Oct 2, 2026
f7d8a31
Reproduce overlapping profile retries deterministically without weake…
sultanasiri001-sys Oct 2, 2026
365e522
Verify center safety isolation and membership revocation on ephemeral…
sultanasiri001-sys Oct 2, 2026
4b4127e
Finalize guarded route normalization and register center safety datab…
sultanasiri001-sys Oct 2, 2026
65b788f
Require center safety HTTP/PostgreSQL isolation regression in API val…
sultanasiri001-sys Oct 2, 2026
e7c2898
Limit one-shot repair to source changes; workflow registration handle…
sultanasiri001-sys Oct 2, 2026
ca21ecb
Normalize primary and secondary center safety actions; remove tempora…
github-actions[bot] Oct 2, 2026
19506c9
Load center safety workspace before dashboard actions in the real app…
sultanasiri001-sys Oct 2, 2026
44c3e44
Guard center safety bootstrap registration and action aliases in stat…
sultanasiri001-sys Oct 2, 2026
0d5f077
Match center documents UI to scoped assets/licenses API contract
sultanasiri001-sys Oct 2, 2026
3abac6e
Route center documents through the center-scoped workspace
sultanasiri001-sys Oct 2, 2026
1997d91
Register scoped center documents before dashboard actions
sultanasiri001-sys Oct 2, 2026
8e659c3
Test center-scoped documents contract, denial and session cleanup
sultanasiri001-sys Oct 2, 2026
3795d23
Guard scoped center documents bootstrap and route in static validation
sultanasiri001-sys Oct 2, 2026
4ef7111
Harden center equipment workspace authorization, history and stale-se…
sultanasiri001-sys Oct 2, 2026
96c6a35
Register scoped center equipment before dashboard actions
sultanasiri001-sys Oct 2, 2026
43a5a47
Route center inventory controls to scoped equipment workspace
sultanasiri001-sys Oct 2, 2026
6920679
Test center equipment scoping, reauthorization and movement history
sultanasiri001-sys Oct 2, 2026
3b87e5c
Guard center equipment bootstrap and scoped inventory route
sultanasiri001-sys Oct 2, 2026
72e0a37
Harden center customer workspace against stale sessions and malformed…
sultanasiri001-sys Oct 2, 2026
06aec89
Register scoped center customers before dashboard actions
sultanasiri001-sys Oct 2, 2026
f37bcc9
Route center customers through scoped customer workspace
sultanasiri001-sys Oct 2, 2026
9d2647d
Test scoped center customers, denial and revocation cleanup
sultanasiri001-sys Oct 2, 2026
99e5c08
Guard center customers bootstrap and scoped route
sultanasiri001-sys Oct 2, 2026
e119487
Harden center trips and booking detail workspace authorization bounda…
sultanasiri001-sys Oct 2, 2026
d60b2af
Register center operations workspace
sultanasiri001-sys Oct 2, 2026
a5bcbc2
Route center trip and booking controls through scoped operations work…
sultanasiri001-sys Oct 2, 2026
a005bed
Test center trip scoping and booking identity reauthorization
sultanasiri001-sys Oct 2, 2026
09ccb4d
Guard center operations bootstrap and scoped trip routes
sultanasiri001-sys Oct 2, 2026
4915d5e
Separate center team and professional views with session fencing
sultanasiri001-sys Oct 2, 2026
a3fca97
Register scoped center team before dashboard actions
sultanasiri001-sys Oct 2, 2026
926091b
Route center team and dive professionals through scoped membership wo…
sultanasiri001-sys Oct 2, 2026
0efba80
Test center team and professional membership boundaries
sultanasiri001-sys Oct 2, 2026
bd7313d
Guard center team bootstrap and professional route
sultanasiri001-sys Oct 2, 2026
de4fc5f
Update static validation for unified scoped center routes
sultanasiri001-sys Oct 2, 2026
bafb82d
Clear scoped center workspace immediately when action reauthorization…
sultanasiri001-sys Oct 2, 2026
f4605a5
Clear scoped center workspace immediately when action reauthorization…
sultanasiri001-sys Oct 2, 2026
c79cbc3
Keep scoped center review fixtures from shadowing authoritative profi…
sultanasiri001-sys Oct 2, 2026
f5945b1
Keep scoped center review fixtures from shadowing authoritative profi…
sultanasiri001-sys Oct 2, 2026
2dfa44c
Keep scoped center review fixtures from shadowing authoritative profi…
sultanasiri001-sys Oct 2, 2026
006fdf0
Update portal acceptance to require scoped center inventory and trips
sultanasiri001-sys Oct 2, 2026
5a2aaf3
Require center booking task to remain inside scoped operations workspace
sultanasiri001-sys Oct 2, 2026
a6e03b4
Update center portal acceptance to scoped license and asset workspace
sultanasiri001-sys Oct 2, 2026
303355c
Normalize center dive-professional Arabic route labels
sultanasiri001-sys Oct 2, 2026
bd63466
Instrument center action reauthorization fixtures deterministically
sultanasiri001-sys Oct 2, 2026
677ef94
Instrument center action reauthorization fixtures deterministically
sultanasiri001-sys Oct 2, 2026
71901c6
Fix Playwright route precedence for center action reauthorization fix…
sultanasiri001-sys Oct 2, 2026
f1e958c
Fix Playwright route precedence for center action reauthorization fix…
sultanasiri001-sys Oct 2, 2026
c15ddae
Use authoritative portal freshness API for scoped center action reaut…
sultanasiri001-sys Oct 2, 2026
3325761
Use authoritative portal freshness API for scoped center action reaut…
sultanasiri001-sys Oct 2, 2026
7a88a29
Serve authoritative profile refresh from one deterministic center tes…
sultanasiri001-sys Oct 2, 2026
bd1ba3a
Serve authoritative profile refresh from one deterministic center tes…
sultanasiri001-sys Oct 2, 2026
40ecbd7
Avoid duplicate portal enforcement side effects during scoped center …
sultanasiri001-sys Oct 2, 2026
f837ba3
Avoid duplicate portal enforcement side effects during scoped center …
sultanasiri001-sys Oct 2, 2026
f361f79
Make scoped center action authorization self-contained and fail closed
sultanasiri001-sys Oct 2, 2026
466417b
Make scoped center action authorization self-contained and fail closed
sultanasiri001-sys Oct 2, 2026
92a5da2
Add focused center action reauthorization regressions
sultanasiri001-sys Oct 2, 2026
6309903
fix(center-equipment): bind recreated panels and fence asynchronous a…
sultanasiri001-sys Oct 2, 2026
a5915bc
fix(center-operations): retain booking handlers through workspace rec…
sultanasiri001-sys Oct 2, 2026
2afdab6
test(center): cover recreated handlers, awaited lookup and stale auth…
sultanasiri001-sys Oct 2, 2026
9d7108a
test(center): assert stale data absence on the recreated workspace su…
sultanasiri001-sys Oct 2, 2026
b0e0b28
ci(web): preserve full-suite visual review artifacts
sultanasiri001-sys Oct 2, 2026
a32470c
fix(center-customers): reauthorize before sensitive customer reads
sultanasiri001-sys Oct 2, 2026
92fa5f4
fix(center-team): reauthorize before member identity reads
sultanasiri001-sys Oct 2, 2026
0c0f822
test(center-customers): deny identity read after role revocation
sultanasiri001-sys Oct 2, 2026
277c5ef
test(center-team): deny member read after role revocation
sultanasiri001-sys Oct 2, 2026
07baaca
fix(center-documents): reauthorize before scoped document reads
sultanasiri001-sys Oct 2, 2026
e331885
test(center-documents): deny scoped reads after role revocation
sultanasiri001-sys Oct 2, 2026
78a9418
fix(center-safety): reauthorize before scoped safety reads
sultanasiri001-sys Oct 2, 2026
25a05e2
test(center-safety): deny records after role revocation
sultanasiri001-sys Oct 2, 2026
af975d5
fix(center-equipment): reauthorize before inventory reads
sultanasiri001-sys Oct 2, 2026
2bb1fb7
fix(center-operations): reauthorize before trip reads
sultanasiri001-sys Oct 2, 2026
6870750
test(center-equipment): deny inventory after role revocation
sultanasiri001-sys Oct 2, 2026
4b57aa1
test(center-operations): deny trip reads after role revocation
sultanasiri001-sys Oct 2, 2026
aac6270
test(web): enforce fresh authorization across center modules
sultanasiri001-sys Oct 2, 2026
08f2b29
test(center): exercise module authorization without disabled navigati…
sultanasiri001-sys Oct 2, 2026
eebe688
test(center): exercise module authorization without disabled navigati…
sultanasiri001-sys Oct 2, 2026
4aa15f0
test(center): exercise module authorization without disabled navigati…
sultanasiri001-sys Oct 2, 2026
c9b7e3f
test(center): exercise module authorization without disabled navigati…
sultanasiri001-sys Oct 2, 2026
dc37269
test(center): exercise module authorization without disabled navigati…
sultanasiri001-sys Oct 2, 2026
e9a9e62
test(center): exercise module authorization without disabled navigati…
sultanasiri001-sys Oct 2, 2026
8be15b2
test(center): count authoritative role refreshes
sultanasiri001-sys Oct 2, 2026
294f069
test(center): count authoritative role refreshes
sultanasiri001-sys Oct 2, 2026
038e982
test(center): count authoritative role refreshes
sultanasiri001-sys Oct 2, 2026
0e702a0
test(center): count authoritative role refreshes
sultanasiri001-sys Oct 2, 2026
9b7d853
test(center): avoid stale-workspace authorization deadlock
sultanasiri001-sys Oct 2, 2026
d3ef75a
test(center): model authoritative revocation before direct module entry
sultanasiri001-sys Oct 2, 2026
1a513aa
test(center): model authoritative revocation before direct module entry
sultanasiri001-sys Oct 2, 2026
a491451
test(center): model authoritative revocation before direct module entry
sultanasiri001-sys Oct 2, 2026
a019e96
test(center): model authoritative revocation before direct module entry
sultanasiri001-sys Oct 2, 2026
372feae
test(center): distinguish fresh replacement authorization from stale …
sultanasiri001-sys Oct 2, 2026
fed2bf5
refactor(auth): centralize protected role authorization
sultanasiri001-sys Oct 2, 2026
b075bd7
refactor(center-customers): use centralized role authorization
sultanasiri001-sys Oct 2, 2026
9a86b82
refactor(center-documents): use centralized role authorization
sultanasiri001-sys Oct 2, 2026
b0d4d26
refactor(center-safety): use centralized role authorization
sultanasiri001-sys Oct 2, 2026
381e152
refactor(center-team): use centralized role authorization
sultanasiri001-sys Oct 2, 2026
040c3e5
refactor(center-equipment): use centralized role authorization
sultanasiri001-sys Oct 2, 2026
d711209
refactor(center-operations): use centralized role authorization
sultanasiri001-sys Oct 2, 2026
48d1a73
refactor(portal): route dashboard actions through central authorization
sultanasiri001-sys Oct 2, 2026
a4af9d4
test(auth): enforce centralized portal authorization architecture
sultanasiri001-sys Oct 2, 2026
b3645af
refactor(role-tasks): use centralized portal authorization
sultanasiri001-sys Oct 2, 2026
6f185c2
test(auth): align role-task validation with central authorization
sultanasiri001-sys Oct 2, 2026
ee90131
fix(web): stabilize protected module bootstrap order
sultanasiri001-sys Oct 2, 2026
8de5ae6
fix(web): remove late duplicate portal bootstrap
sultanasiri001-sys Oct 2, 2026
7dfa4ac
test(portal): validate centralized freshness bootstrap
sultanasiri001-sys Oct 2, 2026
f73d533
test(auth): lock centralized bootstrap order
sultanasiri001-sys Oct 2, 2026
e5cfd94
test(auth): seed sessions through the production session contract
sultanasiri001-sys Oct 2, 2026
846081b
test(center): assert coalesced centralized role refresh
sultanasiri001-sys Oct 2, 2026
9d7cc85
fix(auth): resync role controls when authoritative profile arrives
sultanasiri001-sys Oct 2, 2026
293ff2f
test(auth): require role resync after profile publication
sultanasiri001-sys Oct 2, 2026
06d31de
fix(instructor): route professional portal to scoped training modes
sultanasiri001-sys Oct 2, 2026
79ebee1
fix(instructor): expose professional profile as a real portal target
sultanasiri001-sys Oct 2, 2026
9b40feb
test(instructor): exercise scoped professional portal routes
sultanasiri001-sys Oct 2, 2026
a8afc03
test(instructor): lock scoped professional routing
sultanasiri001-sys Oct 2, 2026
8047705
fix(instructor): stabilize scoped training workspace transitions
sultanasiri001-sys Oct 2, 2026
bbf91a9
test(instructor): navigate scoped routes through mobile portal shell
sultanasiri001-sys Oct 2, 2026
cbb17f8
fix(profile): bind superseded loads to their replacement generation
sultanasiri001-sys Oct 2, 2026
1507ce7
fix(workspace): isolate managed professional training from public tra…
sultanasiri001-sys Oct 2, 2026
5caf4a9
fix(workspace): promote professional training public root for managed…
sultanasiri001-sys Oct 2, 2026
fc40a27
fix(workspace): define managed professional training visibility contract
sultanasiri001-sys Oct 2, 2026
26384ba
test(workspace): lock managed professional training visibility contract
sultanasiri001-sys Oct 2, 2026
9369de8
fix(workspace): select the real managed training root under main
sultanasiri001-sys Oct 2, 2026
c9caa50
fix(workspace): style the actual managed training service root
sultanasiri001-sys Oct 2, 2026
97c9cd8
test(workspace): require real managed training root resolution
sultanasiri001-sys Oct 2, 2026
2a2bc02
fix(workspace): refresh role policy before service visibility checks
sultanasiri001-sys Oct 2, 2026
544fc5d
test(workspace): enforce fresh role policy before service selection
sultanasiri001-sys Oct 2, 2026
2414ece
fix(workspace): make selected service persistent across policy refreshes
sultanasiri001-sys Oct 2, 2026
3127ca3
test(workspace): require selected-service state persistence
sultanasiri001-sys Oct 2, 2026
595db7d
test(instructor): capture runtime visibility ownership diagnostics
sultanasiri001-sys Oct 2, 2026
d5bcb8b
fix(instructor): route managed training to the actual training servic…
sultanasiri001-sys Oct 2, 2026
2001203
test(instructor): require actual managed training service target
sultanasiri001-sys Oct 2, 2026
25f2c83
test(instructor): remove resolved training visibility probe
sultanasiri001-sys Oct 2, 2026
01fdd4f
test(instructor): probe workspace show contract at runtime
sultanasiri001-sys Oct 2, 2026
7de0896
fix(instructor): authorize scoped training against the managed servic…
sultanasiri001-sys Oct 2, 2026
d48d9c2
test(instructor): remove resolved workspace contract probe
sultanasiri001-sys Oct 2, 2026
2a08e47
test(instructor): lock managed training availability ownership
sultanasiri001-sys Oct 2, 2026
2db2413
fix(instructor): separate training capability from current workspace …
sultanasiri001-sys Oct 2, 2026
274c510
test(instructor): forbid visibility-dependent training capability
sultanasiri001-sys Oct 2, 2026
b7a2399
test(instructor): capture actual nav capability state before click
sultanasiri001-sys Oct 2, 2026
640b4c5
fix(instructor): resolve scoped nav routes before target guard
sultanasiri001-sys Oct 3, 2026
fb78b43
test(instructor): remove resolved nav runtime probe
sultanasiri001-sys Oct 3, 2026
3cabaae
test(instructor): lock scoped nav target resolution before guard
sultanasiri001-sys Oct 3, 2026
baabe57
fix(portal): let controller-owned routes bypass generic DOM target gu…
sultanasiri001-sys Oct 3, 2026
9e2dbf2
test(portal): lock controller-owned route dispatch contract
sultanasiri001-sys Oct 3, 2026
bf19ee4
fix(center): enforce active portal role before scoped reads and writes
sultanasiri001-sys Oct 3, 2026
7e8487f
fix(center): enforce equipment inspection and atomic movement audit
sultanasiri001-sys Oct 3, 2026
a9633d5
fix(documents): reject lifecycle transitions after concurrent revision
sultanasiri001-sys Oct 3, 2026
252845c
feat(center): attach license files and display validity and internal …
sultanasiri001-sys Oct 3, 2026
82b5647
test(center): revoke license download access after panel is ready
sultanasiri001-sys Oct 3, 2026
3c78975
feat(center): create and renew scoped license drafts with audit prove…
sultanasiri001-sys Oct 3, 2026
2a7d54d
feat(center): complete scoped license registration and internal revie…
sultanasiri001-sys Oct 3, 2026
c5b8d9a
fix(center): retain reviewer names after eligibility changes
sultanasiri001-sys Oct 3, 2026
f48d713
test(center): isolate review submission authorization from focus refresh
sultanasiri001-sys Oct 3, 2026
b17f023
test(portal): observe professional role loss before switching workspaces
sultanasiri001-sys Oct 3, 2026
41b0cd3
fix(center): reject registration after concurrent license edits
sultanasiri001-sys Oct 3, 2026
ea81833
test(web): observe role revocation before reopening switcher
sultanasiri001-sys Oct 3, 2026
ff9f10d
fix(deploy): isolate API runtime from mobile dependency graph
sultanasiri001-sys Oct 3, 2026
5322463
ci(release): independently gate web API and mobile dependencies
sultanasiri001-sys Oct 3, 2026
ee6b846
build(render): validate isolated native API and web release commands
sultanasiri001-sys Oct 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/api-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm ci --workspace=@hydroland/api --include-workspace-root=true
- run: npm --workspace @hydroland/api run db:generate
# Legacy migration history contains pre-production migrations whose original SQL
# cannot bootstrap a clean database (for example TEXT -> UUID FK drift).
Expand All @@ -37,6 +37,8 @@ jobs:
# Forward migration immutability is enforced separately by Production Release Gate.
- name: Bootstrap canonical schema on clean PostgreSQL
run: npm --workspace @hydroland/api exec -- prisma db push --skip-generate
- name: Center license forward migration on TEXT and UUID asset keys
run: node apps/api/scripts/center-license-migration-db-e2e.mjs
- name: Bootstrap raw equipment operational schema
run: node apps/api/scripts/bootstrap-equipment-runtime-schema.mjs
- name: Bootstrap trip weather operational schema
Expand Down Expand Up @@ -78,8 +80,8 @@ jobs:
- run: npm run validate:translation -w @hydroland/api
- run: npm run validate:marine -w @hydroland/api
- run: npm run validate:integrations -w @hydroland/api
- run: npm run typecheck
- run: npm run build
- run: npm run typecheck --workspace=@hydroland/api
- run: npm run build --workspace=@hydroland/api
- name: Credential upload payload and legacy schema regression
run: node apps/api/scripts/credential-upload-regression.mjs
- name: Credential storage configuration and readiness
Expand Down Expand Up @@ -145,6 +147,7 @@ jobs:
MARINE_DOCS_E2E_BASE_URL=http://127.0.0.1:3101/api/v1 node apps/api/scripts/marine-documents-http-e2e.mjs
MARINE_READINESS_E2E_BASE_URL=http://127.0.0.1:3101/api/v1 node apps/api/scripts/marine-readiness-http-e2e.mjs
SAFETY_CHECKLIST_E2E_BASE_URL=http://127.0.0.1:3101/api/v1 node apps/api/scripts/safety-checklist-http-e2e.mjs
CENTER_SAFETY_E2E_BASE_URL=http://127.0.0.1:3101/api/v1 node apps/api/scripts/center-safety-http-e2e.mjs
SAFETY_INCIDENTS_E2E_BASE_URL=http://127.0.0.1:3101/api/v1 node apps/api/scripts/safety-incidents-http-e2e.mjs
if ! PROFILE_E2E_BASE_URL=http://127.0.0.1:3101/api/v1 node apps/api/scripts/profile-account-http-e2e.mjs; then
echo "Profile/Account HTTP/DB E2E failed; API log follows"
Expand Down
43 changes: 13 additions & 30 deletions .github/workflows/dependency-lock.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ on:
pull_request:
paths:
- 'package.json'
- 'package-lock.json'
- 'apps/**/package.json'
- 'scripts/release-dependency-audit*'
- '.github/workflows/dependency-lock.yml'
workflow_dispatch:

permissions:
contents: read

jobs:
lock-and-audit:
runs-on: ubuntu-24.04
Expand All @@ -16,39 +21,17 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Generate deterministic lockfile
run: npm install --package-lock-only --ignore-scripts
- name: Audit high and critical vulnerabilities
run: |
npm audit --json > audit.json || true
node - <<'NODE'
const fs = require('fs');
const report = JSON.parse(fs.readFileSync('audit.json', 'utf8'));
const vulnerabilities = report.vulnerabilities || {};
const rows = Object.entries(vulnerabilities)
.filter(([, v]) => ['high', 'critical'].includes(v.severity))
.map(([name, v]) => ({
name,
severity: v.severity,
direct: v.isDirect,
range: v.range,
fixAvailable: v.fixAvailable,
via: (v.via || []).map(x => typeof x === 'string' ? x : {
source: x.source,
name: x.name,
title: x.title,
severity: x.severity,
range: x.range
})
}));
console.log(JSON.stringify({ metadata: report.metadata?.vulnerabilities || {}, highOrCritical: rows }, null, 2));
if (rows.length) process.exitCode = 1;
NODE
- name: Upload lockfile and audit report
- name: Validate committed lock by clean scoped installation
run: npm ci --workspace=@hydroland/api --workspace=@hydroland/web --include-workspace-root=true --ignore-scripts
- name: Reject lockfile mutation
run: git diff --exit-code -- package-lock.json
- name: Audit web/API release lock
run: node scripts/release-dependency-audit.mjs web-api audit.json
- name: Upload committed lockfile and audit report
if: always()
uses: actions/upload-artifact@v4
with:
name: dependency-lock-audit
name: web-api-lock-audit
path: |
package-lock.json
audit.json
Expand Down
36 changes: 36 additions & 0 deletions .github/workflows/mobile-security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Mobile release security (independent release)

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
mobile-security:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci --ignore-scripts
- name: Verify fail-closed audit contract
run: node scripts/release-dependency-audit.test.mjs
- name: Block vulnerable mobile releases including build tools
run: node scripts/release-dependency-audit.mjs mobile mobile-audit.json
- name: Retain full repository security coverage
if: always()
run: node scripts/release-dependency-audit.mjs repository repository-audit.json
- name: Upload mobile and full repository reports even on failure
if: always()
uses: actions/upload-artifact@v4
with:
name: mobile-and-repository-security
path: |
mobile-audit.json
repository-audit.json
retention-days: 7
2 changes: 1 addition & 1 deletion .github/workflows/mobile-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm ci --workspace=@hydroland/mobile --include-workspace-root=false
- run: npm --workspace @hydroland/mobile run test
- run: npm --workspace @hydroland/mobile run typecheck
- run: npm --workspace @hydroland/mobile run build
7 changes: 5 additions & 2 deletions .github/workflows/pages-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ jobs:
node-version: 22

- name: Install dependencies
run: npm ci
run: npm ci --workspace=@hydroland/web --include-workspace-root=true --ignore-scripts

- name: Validate
run: npm run typecheck
run: npm run test --workspace=@hydroland/web

- name: Audit web/API release before publishing web artifact
run: node scripts/release-dependency-audit.mjs web-api audit.json

- name: Build web
run: npm run build --workspace @hydroland/web
Expand Down
95 changes: 94 additions & 1 deletion .github/workflows/phase11-production-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ on:
- 'apps/api/src/main.ts'
- 'apps/api/src/auth/**'
- 'apps/api/scripts/phase11-production-security-validation.mjs'
- 'apps/api/scripts/runtime-dependency-boundary.mjs'
- 'apps/api/package.json'
- 'package.json'
- 'package-lock.json'
- 'infra/**'
- '.github/workflows/security-audit.yml'
- '.github/workflows/mobile-security.yml'
- 'scripts/release-dependency-audit*'
- 'scripts/build-*-release.mjs'
- '.github/workflows/phase11-production-security.yml'
pull_request:
paths:
Expand All @@ -19,14 +26,34 @@ on:
- 'apps/api/src/main.ts'
- 'apps/api/src/auth/**'
- 'apps/api/scripts/phase11-production-security-validation.mjs'
- 'apps/api/scripts/runtime-dependency-boundary.mjs'
- 'apps/api/package.json'
- 'package.json'
- 'package-lock.json'
- 'infra/**'
- '.github/workflows/security-audit.yml'
- '.github/workflows/mobile-security.yml'
- 'scripts/release-dependency-audit*'
- 'scripts/build-*-release.mjs'
- '.github/workflows/phase11-production-security.yml'
workflow_dispatch:

jobs:
validate:
runs-on: ubuntu-24.04
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: hydroland
POSTGRES_PASSWORD: hydroland
POSTGRES_DB: hydroland_image_test
ports: ['5432:5432']
options: >-
--health-cmd "pg_isready -U hydroland -d hydroland_image_test"
--health-interval 5s --health-timeout 5s --health-retries 10
env:
DATABASE_URL: postgresql://hydroland:hydroland@127.0.0.1:5432/hydroland_image_test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
Expand All @@ -36,6 +63,72 @@ jobs:
run: node apps/api/scripts/phase11-production-security-validation.mjs
- name: Build production API image
run: docker build -t hydroland-api:phase11 .
- name: Build Compose API image
run: docker build -f apps/api/Dockerfile -t hydroland-api:compose .
- name: Verify image runs as non-root user
run: |
test "$(docker image inspect hydroland-api:phase11 --format '{{.Config.User}}')" = "node"
for image in hydroland-api:phase11 hydroland-api:compose; do
test "$(docker image inspect "$image" --format '{{.Config.User}}')" = "node"
done
- name: Verify installed runtime boundary and offline Prisma CLI
run: |
for image in hydroland-api:phase11 hydroland-api:compose; do
label="${image##*:}"
docker run --rm --network none --read-only --tmpfs /tmp --entrypoint node "$image" \
apps/api/scripts/runtime-dependency-boundary.mjs > "api-runtime-$label.json"
docker run --rm --network none --read-only --tmpfs /tmp --entrypoint node "$image" \
node_modules/prisma/build/index.js --version
done
- name: Audit API image dependencies (additional to full repository gate)
run: |
docker run --rm --entrypoint npm hydroland-api:phase11 \
audit --workspace=@hydroland/api --include-workspace-root=false --omit=dev --audit-level=high
- name: Verify final image starts and queries disposable PostgreSQL
run: |
# Historical migrations cannot bootstrap an empty database. This smoke
# checks runtime/engine readiness, not production migration acceptance.
docker run --rm --network host -e DATABASE_URL --entrypoint node hydroland-api:phase11 \
node_modules/prisma/build/index.js db push --schema apps/api/prisma/schema.prisma --skip-generate
docker run -d --name hydroland-image-smoke --network host --read-only --tmpfs /tmp \
-e DATABASE_URL -e PORT=3102 -e WEB_ORIGIN=http://127.0.0.1:4173 \
-e JWT_SECRET=ci-image-smoke-secret-at-least-32-characters \
--entrypoint node hydroland-api:phase11 apps/api/dist/main.js
trap 'docker logs hydroland-image-smoke; docker rm -f hydroland-image-smoke' EXIT
for attempt in {1..30}; do
if curl -fsS http://127.0.0.1:3102/api/v1/health/ready > /tmp/api-image-ready.json; then
node -e 'const r=require("/tmp/api-image-ready.json"); if(r.status!=="ready"||r.database!=="ok")process.exit(1)'
exit 0
fi
sleep 1
done
exit 1
- name: Verify native Render API build command and retained Prisma engine
run: |
cd apps/api
node ../../scripts/build-api-release.mjs > /tmp/native-api-build.log 2>&1 || { cat /tmp/native-api-build.log; exit 1; }
node ../../node_modules/prisma/build/index.js --version
node ../../node_modules/prisma/build/index.js db push --schema prisma/schema.prisma --skip-generate
JWT_SECRET=ci-native-api-secret-at-least-32-characters WEB_ORIGIN=http://127.0.0.1:4173 PORT=3103 NODE_ENV=production \
node dist/main.js > /tmp/native-api.log 2>&1 &
api_pid=$!
trap 'cat /tmp/native-api.log; kill "$api_pid" || true' EXIT
for attempt in {1..30}; do
if curl -fsS http://127.0.0.1:3103/api/v1/health/ready > /tmp/native-api-ready.json; then
node -e 'const r=require("/tmp/native-api-ready.json"); if(r.status!=="ready"||r.database!=="ok")process.exit(1)'
exit 0
fi
sleep 1
done
exit 1
- name: Verify native Render web build command
run: node scripts/build-web-release.mjs
- name: Retain installed runtime inventories
if: always()
uses: actions/upload-artifact@v4
with:
name: api-runtime-inventories
path: |
api-runtime-*.json
api-release-audit.json
web-release-audit.json
retention-days: 7
8 changes: 4 additions & 4 deletions .github/workflows/production-release-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm ci --workspace=@hydroland/api --workspace=@hydroland/web --include-workspace-root=true
- run: npm --workspace @hydroland/api run db:generate
- name: Verify production-applied migration checksums
run: node apps/api/scripts/production-migration-baseline-validation.mjs
Expand All @@ -40,11 +40,11 @@ jobs:
npm run validate:translation -w @hydroland/api
npm run validate:marine -w @hydroland/api
- name: Test
run: npm test
run: npm test --workspace=@hydroland/api --workspace=@hydroland/web --if-present
- name: Typecheck
run: npm run typecheck
run: npm run typecheck --workspace=@hydroland/api --workspace=@hydroland/web --if-present
- name: Build
run: npm run build
run: npm run build --workspace=@hydroland/api --workspace=@hydroland/web --if-present
- name: Confirm production remains explicit
shell: bash
run: |
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/release-candidate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm ci --workspace=@hydroland/api --workspace=@hydroland/web --include-workspace-root=true
- run: npm --workspace @hydroland/api run db:generate
- name: API domain validations
run: |
Expand All @@ -29,9 +29,9 @@ jobs:
npm run validate:translation -w @hydroland/api
npm run validate:marine -w @hydroland/api
npm run validate:integrations -w @hydroland/api
- name: Workspace tests
run: npm test
- name: Typecheck all workspaces
run: npm run typecheck
- name: Build all workspaces
run: npm run build
- name: Web/API release tests
run: npm test --workspace=@hydroland/api --workspace=@hydroland/web --if-present
- name: Typecheck web/API release
run: npm run typecheck --workspace=@hydroland/api --workspace=@hydroland/web --if-present
- name: Build web/API release
run: npm run build --workspace=@hydroland/api --workspace=@hydroland/web --if-present
32 changes: 12 additions & 20 deletions .github/workflows/security-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,29 @@ on:
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
audit:
# User-approved release boundary: web/API/root, including build tools.
# Mobile and the full repository retain a separate failing security gate.
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: npm ci
- name: Generate dependency audit report and enforce severity gate
shell: bash
run: |
npm audit --json > audit.json || true
node - <<'NODE'
const fs=require('fs');
const report=JSON.parse(fs.readFileSync('audit.json','utf8'));
const vulns=report.vulnerabilities||{};
const rows=Object.entries(vulns)
.filter(([,v])=>['high','critical'].includes(v.severity))
.map(([name,v])=>({name,severity:v.severity,direct:v.isDirect,range:v.range,fixAvailable:v.fixAvailable,via:(v.via||[]).map(x=>typeof x==='string'?x:{source:x.source,name:x.name,title:x.title,severity:x.severity,range:x.range})}));
console.log(JSON.stringify({metadata:report.metadata?.vulnerabilities||{},highOrCritical:rows},null,2));
if(rows.length){
console.error(`Security gate failed: ${rows.length} high/critical package vulnerabilities detected.`);
process.exit(1);
}
NODE
- name: Install web and API release dependencies
run: npm ci --workspace=@hydroland/api --workspace=@hydroland/web --include-workspace-root=true
- name: Verify fail-closed audit contract
run: node scripts/release-dependency-audit.test.mjs
- name: Audit web/API release including build dependencies
run: node scripts/release-dependency-audit.mjs web-api audit.json
- name: Upload audit report
if: always()
uses: actions/upload-artifact@v4
with:
name: npm-audit-report
name: web-api-dependency-audit
path: audit.json
retention-days: 7
Loading
Loading