Skip to content

feat(core): policy-core extraction + self-host kit + fgpack workflow#5

Merged
razvantirboaca merged 2 commits intomainfrom
feat/policy-core-selfhost-kit
Feb 5, 2026
Merged

feat(core): policy-core extraction + self-host kit + fgpack workflow#5
razvantirboaca merged 2 commits intomainfrom
feat/policy-core-selfhost-kit

Conversation

@razvantirboaca
Copy link
Member

@razvantirboaca razvantirboaca commented Feb 5, 2026

What this adds

  • Extracts vault policy logic into shared policyCore module (portable, deterministic).
  • Adds fail-closed vault schema validation on /api/vault/resolve.
  • Adds Membrane Gate UI (4.5s hold) with RO/ES/EN copy.
  • Adds self-host kit: docker-compose.yml, .env.selfhost.example, Makefile.
  • Adds .fgpack docs + export/verify scripts for lineage portability.
  • Adds deterministic tests for policy decisions and entry-ref resolution.

Follow-up fixes in this PR

  • Stabilizes dev server startup for ESM + ts-node workspaces.
  • Resolves Cannot find module .../shared/src/policyCore by using ESM-safe specifier + package type metadata.
  • Pins root package manager to yarn@1.22.22 for Corepack consistency.

Why

This moves Firegate closer to protocol-grade resilience: policy is decoupled from framework code, self-host path is one-command friendly, and memory continuity has a verifiable archive format.

Validation

  • npx tsc --noEmit
  • npx vitest run shared/src/__tests__/policyCore.test.ts
  • VIP_VAULT_MAP_PATH=/Users/raz/Desktop/VIP/vault_map.json yarn dev
  • node scripts/fgpack-export.mjs --vault-root /Users/raz/Desktop/VIP --out /tmp/firegate-test.fgpack
  • node scripts/fgpack-verify.mjs --pack /tmp/firegate-test.fgpack

Assembly Notes

  • Pieces changed: UI entry membrane, server vault resolve, shared policy core, self-host and archive tooling.
  • Fasteners changed: runtime gates + schema validation + self-host env contract (VIP_VAULT_DIR).
  • A11y/theming: gate supports keyboard hold interaction and uses existing i18n tone.

- move vault resolve decision logic into shared policyCore with deterministic tests

- add fail-closed vault schema validation on /api/vault/resolve

- add membrane entry gate (4.5s hold) with i18n strings

- add docker-compose + Makefile self-host workflow and fgpack export/verify scripts

🌬️ whisper: freedom should be runnable.
@razvantirboaca razvantirboaca merged commit 6efdc74 into main Feb 5, 2026
5 checks passed
@razvantirboaca razvantirboaca deleted the feat/policy-core-selfhost-kit branch February 5, 2026 02:26
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extracts vault policy logic into a portable, deterministic policyCore module and adds comprehensive self-hosting infrastructure for Firegate. The changes move the system toward protocol-grade resilience by decoupling policy decisions from framework code, adding fail-closed validation, and providing a one-command self-host path with verifiable archive format (.fgpack).

Changes:

  • Extracts and centralizes policy logic (gate thresholds, runtime decisions, vault validation) into shared policyCore module with deterministic test coverage
  • Adds Membrane Gate UI component with 4.5s hold interaction and anti-frantic safeguards (keyboard accessible, i18n-aware)
  • Introduces .fgpack archive format with export/verify scripts for lineage portability and memory continuity
  • Provides Docker Compose self-host kit with Makefile targets for one-command deployment and backup workflows

Reviewed changes

Copilot reviewed 19 out of 20 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
shared/src/policyCore.ts New core module exporting vault policy logic including types, validation, entry resolution, and runtime decision computation
shared/src/index.js Adds export for policyCore module to shared package
shared/src/tests/policyCore.test.ts Adds deterministic tests for validation, runtime decisions, and entry resolution
packages/server/index.ts Refactored to use shared policyCore functions, adds fail-closed vault schema validation
packages/ui/src/features/Firegate/index.tsx Integrates MembraneGate component and imports DEFAULT_GATES from policyCore
packages/ui/src/features/Firegate/components/MembraneGate.tsx New entry gate UI with hold interaction, frantic-detection, and keyboard accessibility
shared/src/i18n/en.json Adds English translations for membrane gate UI
shared/src/i18n/es.json Adds Spanish translations for membrane gate UI
shared/src/i18n/ro.json Adds Romanian translations for membrane gate UI
scripts/fgpack-export.mjs New script to export vault archives to .fgpack format with SHA256 checksums
scripts/fgpack-verify.mjs New script to verify .fgpack archive integrity via hash verification
docs/fgpack.md Documentation for .fgpack archive format and security model
docker-compose.yml Docker Compose configuration for self-hosted server and UI services
Makefile Build targets for Docker operations, backup, verify, and policy tests
.env.selfhost.example Example environment configuration for self-hosting
README.md Adds self-host quickstart guide and references to new documentation
.gitignore Adds exports/ directory to ignore list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

SHOUT_STATIC_LEVEL,
} from './lib/somatic';
import { StaticGrainOverlay } from './components/StaticGrainOverlay';
import { SignalQuality, type SignalStatus } from './components/SignalQuality';
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SignalStatus is now exported from the shared policyCore module (line 1 of shared/src/policyCore.ts), but this file imports it from the local SignalQuality component. For consistency and maintainability, consider importing SignalStatus from '@shared/policyCore' to use the canonical type definition, especially since DEFAULT_GATES is already being imported from that module on line 39.

Copilot uses AI. Check for mistakes.
Comment on lines +54 to +56
const actual = await sha256File(fullPath);
if (actual !== expected) {
errors.push(`hash mismatch for ${relPath}: expected ${expected}, got ${actual}`);
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a file listed in the manifest doesn't exist at the expected path, the await sha256File call will throw an error that gets caught by the outer catch handler (line 70-72). This provides a generic error message instead of a specific one indicating which file is missing. Consider wrapping the sha256File call in a try-catch to collect a more descriptive error message about the missing file, similar to how invalid file entries are handled above.

Suggested change
const actual = await sha256File(fullPath);
if (actual !== expected) {
errors.push(`hash mismatch for ${relPath}: expected ${expected}, got ${actual}`);
try {
const actual = await sha256File(fullPath);
if (actual !== expected) {
errors.push(`hash mismatch for ${relPath}: expected ${expected}, got ${actual}`);
}
} catch (error) {
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
errors.push(`missing file for ${relPath}: expected at ${fullPath}`);
} else {
errors.push(
`error reading file for ${relPath}: ${error instanceof Error ? error.message : String(error)}`
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +51
const vaultMap = JSON.parse(await readFile(vaultMapPath, 'utf8'));
const filesSet = new Set(['vault_map.json']);

const levels = vaultMap.focus_levels || {};
for (const level of Object.values(levels)) {
const entries = Array.isArray(level.files) ? level.files : [];
for (const entry of entries) {
if (!entry || typeof entry !== 'object') continue;
if (typeof entry.entry_ref === 'string') filesSet.add(entry.entry_ref);
if (typeof entry.path === 'string' && entry.path.endsWith('.json')) filesSet.add(entry.path);
}
}
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The export script doesn't validate the vault_map structure before proceeding with the export. If the vault_map is malformed or missing required fields, the export may fail partway through or produce an invalid fgpack. Consider using the validateVaultMap function from policyCore (which is now available in the shared module) to fail fast with clear error messages if the vault structure is invalid, as recommended by the fail-closed approach described in the PR description.

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +62
await cp(absPath, destPath);
fileMetas.push(await fileMeta(destPath, relPath));
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a file referenced in the vault_map doesn't exist in the vault directory, the cp command at line 61 will fail and the error will be caught by the outer catch handler. This provides a generic error message rather than clearly indicating which file is missing. Consider wrapping this in a try-catch to provide more specific error messages about which files couldn't be found in the vault, helping users diagnose incomplete vault exports.

Suggested change
await cp(absPath, destPath);
fileMetas.push(await fileMeta(destPath, relPath));
try {
await cp(absPath, destPath);
fileMetas.push(await fileMeta(destPath, relPath));
} catch (error) {
if (error && typeof error === 'object' && error.code === 'ENOENT') {
throw new Error(
`Missing file in vault export: '${relPath}' (expected at '${absPath}')`
);
}
throw error;
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant