docs: Complete Trinity Console 2.0 code package from Gemini

Production-ready code for:

Task #95 - Core Engine:
- modules/registry.js (module storage)
- modules/loader.js (fs discovery + topological sort)
- database/migrations.js (per-module migration runner)
- modules/routes.js (dynamic route mounting)
- events/index.js (EventEmitter registry)
- navigation/index.js (permission-filtered nav builder)
- boot.js (orchestrates full startup sequence)

Task #96 - RBAC Engine:
- 001_rbac_tables.sql (5 tables + indexes + seed data)
- permissions/resolver.js (override → role → deny logic)
- permissions/middleware.js (requirePermission)
- permissions/sync.js (upsert from module.json)
- permissions/routes.js (API endpoints)

Task #98 - Feature Flags:
- 002_feature_flags.sql
- features/index.js (in-memory cache)
- features/middleware.js (requireFeature)
- features/routes.js (toggle API)

Example Dashboard module with all components.

This is the foundation of Trinity Console 2.0.

Signed-off-by: Claude (Chronicler #61) <claude@firefrostgaming.com>
This commit is contained in:
Claude (Chronicler #61)
2026-04-05 11:33:45 +00:00
parent af05287e9e
commit 616271f6b7

View File

@@ -0,0 +1,848 @@
# Gemini Response: Trinity Console 2.0 — Complete Code Package
**Date:** April 5, 2026
**From:** Gemini (Architectural Partner)
**To:** Michael (The Wizard) + Claude (Chronicler #61)
**Re:** Complete, production-ready code for Tasks #95, #96, #98
---
This is the master blueprint, Michael and Chronicler #61. Let's lay the foundation for Trinity Console 2.0.
Here is the complete, production-ready code exactly as requested, formatted for easy copy-pasting into your Arbiter environment.
---
## 1. Database Migrations (Core Foundation)
### `migrations/001_rbac_tables.sql`
```sql
-- 1. Roles Table
CREATE TABLE IF NOT EXISTS roles (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 2. Permissions Table (Updated with is_active)
CREATE TABLE IF NOT EXISTS permissions (
permission_key VARCHAR(100) PRIMARY KEY,
module_id VARCHAR(50) NOT NULL,
name VARCHAR(100) NOT NULL,
description TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 3. Role Permissions Table
CREATE TABLE IF NOT EXISTS role_permissions (
role_id INT REFERENCES roles(id) ON DELETE CASCADE,
permission_key VARCHAR(100) REFERENCES permissions(permission_key) ON DELETE CASCADE,
PRIMARY KEY (role_id, permission_key)
);
-- 4. User Roles Table
CREATE TABLE IF NOT EXISTS user_roles (
user_id VARCHAR(50) PRIMARY KEY,
role_id INT REFERENCES roles(id) ON DELETE CASCADE,
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
assigned_by VARCHAR(50)
);
-- 5. User Permission Overrides Table
CREATE TABLE IF NOT EXISTS user_permission_overrides (
user_id VARCHAR(50) NOT NULL,
permission_key VARCHAR(100) REFERENCES permissions(permission_key) ON DELETE CASCADE,
is_granted BOOLEAN NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by VARCHAR(50),
PRIMARY KEY (user_id, permission_key)
);
-- Core System Migrations Table
CREATE TABLE IF NOT EXISTS core_migrations (
id SERIAL PRIMARY KEY,
module_id VARCHAR(50) NOT NULL,
migration_file VARCHAR(255) NOT NULL,
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(module_id, migration_file)
);
-- Indexes
CREATE INDEX IF NOT EXISTS idx_user_overrides ON user_permission_overrides(user_id);
CREATE INDEX IF NOT EXISTS idx_role_permissions ON role_permissions(role_id);
CREATE INDEX IF NOT EXISTS idx_active_permissions ON permissions(is_active);
-- Seed Data
INSERT INTO roles (name, description) VALUES
('Admin', 'Full system access (The Wizard)'),
('Community', 'Player management and support (The Emissary)'),
('Builder', 'Server and infrastructure management (The Catalyst)')
ON CONFLICT (name) DO NOTHING;
```
### `migrations/002_feature_flags.sql`
```sql
CREATE TABLE IF NOT EXISTS feature_flags (
key VARCHAR(100) PRIMARY KEY,
is_enabled BOOLEAN NOT NULL DEFAULT FALSE,
description TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_by VARCHAR(50)
);
```
---
## 2. Task #95: The Core Engine
### `src/core/modules/registry.js`
```javascript
const modules = new Map();
module.exports = {
register: (moduleConfig) => {
modules.set(moduleConfig.id, moduleConfig);
},
getModule: (id) => modules.get(id),
getAllModules: () => Array.from(modules.values()),
isModuleEnabled: (id) => modules.has(id) // Can be expanded later if dynamic enable/disable is added
};
```
### `src/core/modules/loader.js`
```javascript
const fs = require('fs');
const path = require('path');
function loadModules(modulesPath) {
if (!fs.existsSync(modulesPath)) return [];
const folders = fs.readdirSync(modulesPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
const loadedModules = [];
for (const folder of folders) {
const configPath = path.join(modulesPath, folder, 'module.json');
if (fs.existsSync(configPath)) {
try {
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
config.dirPath = path.join(modulesPath, folder);
loadedModules.push(config);
} catch (err) {
console.error(`[Core] Failed to parse module.json for ${folder}:`, err.message);
}
}
}
return topologicalSort(loadedModules);
}
function topologicalSort(modules) {
const sorted = [];
const visited = new Set();
const visiting = new Set();
const moduleMap = new Map(modules.map(m => [m.id, m]));
function visit(moduleId) {
if (visiting.has(moduleId)) {
throw new Error(`[Core] Fatal: Circular dependency detected involving module '${moduleId}'`);
}
if (visited.has(moduleId)) return;
visiting.add(moduleId);
const mod = moduleMap.get(moduleId);
if (mod && mod.dependencies) {
for (const dep of Object.keys(mod.dependencies)) {
if (!moduleMap.has(dep)) {
console.warn(`[Core] Warning: Module '${moduleId}' depends on missing module '${dep}'`);
continue;
}
visit(dep);
}
}
visiting.delete(moduleId);
visited.add(moduleId);
if (mod) sorted.push(mod);
}
for (const mod of modules) {
visit(mod.id);
}
return sorted;
}
module.exports = { loadModules };
```
### `src/core/database/migrations.js`
```javascript
const fs = require('fs');
const path = require('path');
async function runModuleMigrations(pool, moduleConfig) {
const migrationsDir = path.join(moduleConfig.dirPath, 'migrations');
if (!fs.existsSync(migrationsDir)) return;
const files = fs.readdirSync(migrationsDir)
.filter(f => f.endsWith('.sql'))
.sort();
for (const file of files) {
const checkRes = await pool.query(
'SELECT 1 FROM core_migrations WHERE module_id = $1 AND migration_file = $2',
[moduleConfig.id, file]
);
if (checkRes.rowCount === 0) {
console.log(`[Migrations] Applying ${moduleConfig.id}/${file}...`);
const sql = fs.readFileSync(path.join(migrationsDir, file), 'utf8');
const client = await pool.connect();
try {
await client.query('BEGIN');
await client.query(sql);
await client.query(
'INSERT INTO core_migrations (module_id, migration_file) VALUES ($1, $2)',
[moduleConfig.id, file]
);
await client.query('COMMIT');
} catch (err) {
await client.query('ROLLBACK');
throw new Error(`Migration failed: ${moduleConfig.id}/${file} - ${err.message}`);
} finally {
client.release();
}
}
}
}
module.exports = { runModuleMigrations };
```
### `src/core/modules/routes.js`
```javascript
const express = require('express');
const path = require('path');
const fs = require('fs');
function mountModuleRoutes(app, moduleConfig) {
const routesPath = path.join(moduleConfig.dirPath, 'routes.js');
if (fs.existsSync(routesPath)) {
const router = require(routesPath);
const prefix = moduleConfig.routes || `/${moduleConfig.id}`;
app.use(prefix, router);
console.log(`[Routes] Mounted ${moduleConfig.id} at ${prefix}`);
}
}
module.exports = { mountModuleRoutes };
```
### `src/core/events/index.js`
```javascript
const EventEmitter = require('events');
const fs = require('fs');
const path = require('path');
class CoreEvents extends EventEmitter {}
const coreEmitter = new CoreEvents();
function loadModuleEvents(moduleConfig) {
const eventsPath = path.join(moduleConfig.dirPath, 'events.js');
if (fs.existsSync(eventsPath)) {
const registerEvents = require(eventsPath);
registerEvents(coreEmitter);
console.log(`[Events] Registered events for ${moduleConfig.id}`);
}
}
module.exports = {
emitter: coreEmitter,
loadModuleEvents
};
```
### `src/core/navigation/index.js`
```javascript
const registry = require('../modules/registry');
function buildNavigation(userPermissions) {
const modules = registry.getAllModules();
const navStructure = {};
modules.forEach(mod => {
if (!mod.nav) return;
// Basic view permission check based on module ID (assumes {id}.view convention)
const canView = userPermissions.includes(`${mod.id}.view`) || userPermissions.includes(`${mod.id}.*`);
// If they have admin bypass or specific view permission
if (!canView && !userPermissions.includes('admin.*')) return;
const section = mod.nav.section || 'General';
if (!navStructure[section]) navStructure[section] = [];
navStructure[section].push({
name: mod.name,
icon: mod.icon || 'folder',
path: mod.routes || `/${mod.id}`,
position: mod.nav.position || 99,
badge: mod.nav.badge // Frontend can interpret this string later
});
});
// Sort items within sections
for (const section in navStructure) {
navStructure[section].sort((a, b) => a.position - b.position);
}
return navStructure;
}
module.exports = { buildNavigation };
```
### `src/core/boot.js`
```javascript
const path = require('path');
const { loadModules } = require('./modules/loader');
const registry = require('./modules/registry');
const { runModuleMigrations } = require('./database/migrations');
const { syncPermissions } = require('./permissions/sync');
const { mountModuleRoutes } = require('./modules/routes');
const { loadModuleEvents } = require('./events');
const { refreshCache } = require('./features');
async function bootSystem(app, pool) {
console.log('[Boot] Initializing Trinity Console 2.0...');
const modulesPath = path.join(__dirname, '../../modules');
try {
// 1. Load and Sort Modules
const sortedModules = loadModules(modulesPath);
console.log(`[Boot] Found ${sortedModules.length} valid modules.`);
for (const mod of sortedModules) {
// 2. Register in Memory
registry.register(mod);
// 3. Run Migrations
await runModuleMigrations(pool, mod);
// 4. Register Events
loadModuleEvents(mod);
// 5. Mount Routes
mountModuleRoutes(app, mod);
}
// 6. Sync RBAC Permissions
await syncPermissions(pool, sortedModules);
// 7. Load Feature Flags
await refreshCache(pool);
console.log('[Boot] System fully initialized and ready.');
} catch (err) {
console.error('[Boot] FATAL ERROR during boot sequence:', err);
process.exit(1);
}
}
module.exports = { bootSystem };
```
---
## 3. Task #96: The RBAC Engine
### `src/core/permissions/resolver.js`
```javascript
async function fetchUserPermissions(pool, userId) {
const query = `
SELECT p.permission_key
FROM role_permissions p
JOIN user_roles ur ON p.role_id = ur.role_id
WHERE ur.user_id = $1
UNION
SELECT permission_key
FROM user_permission_overrides
WHERE user_id = $1 AND is_granted = TRUE
EXCEPT
SELECT permission_key
FROM user_permission_overrides
WHERE user_id = $1 AND is_granted = FALSE;
`;
const { rows } = await pool.query(query, [userId]);
return rows.map(r => r.permission_key);
}
function hasPermission(userPermissionsArray, requiredPermission) {
if (!userPermissionsArray || !Array.isArray(userPermissionsArray)) return false;
// Exact match
if (userPermissionsArray.includes(requiredPermission)) return true;
// Wildcard match (e.g., required: 'tasks.edit', user has: 'tasks.*')
const parts = requiredPermission.split('.');
if (parts.length > 1) {
const wildcard = `${parts[0]}.*`;
if (userPermissionsArray.includes(wildcard)) return true;
}
// Super admin wildcard fallback
if (userPermissionsArray.includes('admin.*') || userPermissionsArray.includes('*.*')) return true;
return false;
}
module.exports = { fetchUserPermissions, hasPermission };
```
### `src/core/permissions/middleware.js`
```javascript
const { hasPermission, fetchUserPermissions } = require('./resolver');
const db = require('../../db'); // Adjust path to your db pool connection
function requirePermission(requiredKey) {
return async (req, res, next) => {
if (!req.user) {
return res.status(401).send('Unauthorized');
}
// Cache resolution in session
if (!req.session.permissions) {
req.session.permissions = await fetchUserPermissions(db, req.user.id);
}
if (hasPermission(req.session.permissions, requiredKey)) {
return next();
}
// Handle denial gracefully based on request type
const isHtmx = req.headers['hx-request'] === 'true';
const msg = `Access Denied: Requires '${requiredKey}' permission.`;
if (isHtmx) {
return res.status(403).send(`
<div class="p-4 bg-red-900 border border-red-500 text-white rounded">
<strong>Error:</strong> ${msg}
</div>
`);
} else {
return res.status(403).render('error', { message: msg }); // Adjust to your error template
}
};
}
module.exports = { requirePermission };
```
### `src/core/permissions/sync.js`
```javascript
async function syncPermissions(pool, modules) {
console.log('[RBAC] Syncing permissions from module.json files...');
const allDeclaredPermissions = [];
modules.forEach(mod => {
if (mod.permissions && Array.isArray(mod.permissions)) {
mod.permissions.forEach(p => {
allDeclaredPermissions.push({
key: p.key,
moduleId: mod.id,
name: p.name,
description: p.description
});
});
}
});
const client = await pool.connect();
try {
await client.query('BEGIN');
// 1. Mark everything as inactive first
await client.query('UPDATE permissions SET is_active = FALSE');
// 2. Upsert declared permissions and mark active
for (const p of allDeclaredPermissions) {
await client.query(`
INSERT INTO permissions (permission_key, module_id, name, description, is_active)
VALUES ($1, $2, $3, $4, TRUE)
ON CONFLICT (permission_key) DO UPDATE
SET name = EXCLUDED.name,
description = EXCLUDED.description,
is_active = TRUE;
`, [p.key, p.moduleId, p.name, p.description]);
}
await client.query('COMMIT');
console.log(`[RBAC] Synced ${allDeclaredPermissions.length} permissions successfully.`);
} catch (err) {
await client.query('ROLLBACK');
console.error('[RBAC] Failed to sync permissions:', err);
throw err;
} finally {
client.release();
}
}
module.exports = { syncPermissions };
```
### `src/core/permissions/routes.js`
```javascript
const express = require('express');
const router = express.Router();
const db = require('../../db');
const { requirePermission } = require('./middleware');
// Protect all RBAC routes
router.use(requirePermission('system.rbac.manage'));
router.get('/api/permissions', async (req, res) => {
const { rows } = await db.query('SELECT * FROM permissions WHERE is_active = TRUE ORDER BY module_id, name');
res.json(rows);
});
router.get('/api/roles', async (req, res) => {
const { rows } = await db.query('SELECT * FROM roles ORDER BY id');
res.json(rows);
});
router.post('/api/users/:id/role', async (req, res) => {
const { role_id } = req.body;
await db.query(
`INSERT INTO user_roles (user_id, role_id, assigned_by) VALUES ($1, $2, $3)
ON CONFLICT (user_id) DO UPDATE SET role_id = EXCLUDED.role_id, assigned_by = EXCLUDED.assigned_by`,
[req.params.id, role_id, req.user.id]
);
res.sendStatus(200);
});
router.post('/api/users/:id/override', async (req, res) => {
const { permission_key, is_granted } = req.body;
await db.query(
`INSERT INTO user_permission_overrides (user_id, permission_key, is_granted, created_by)
VALUES ($1, $2, $3, $4)
ON CONFLICT (user_id, permission_key) DO UPDATE
SET is_granted = EXCLUDED.is_granted, created_by = EXCLUDED.created_by`,
[req.params.id, permission_key, is_granted, req.user.id]
);
res.sendStatus(200);
});
router.delete('/api/users/:id/override/:key', async (req, res) => {
await db.query(
'DELETE FROM user_permission_overrides WHERE user_id = $1 AND permission_key = $2',
[req.params.id, req.params.key]
);
res.sendStatus(200);
});
module.exports = router;
```
---
## 4. Task #98: Feature Flags
### `src/core/features/index.js`
```javascript
let featureCache = {};
async function refreshCache(pool) {
try {
const { rows } = await pool.query('SELECT key, is_enabled FROM feature_flags');
const newCache = {};
rows.forEach(r => { newCache[r.key] = r.is_enabled; });
featureCache = newCache;
console.log('[Features] Feature flag cache refreshed.');
} catch (err) {
console.error('[Features] Failed to refresh cache:', err);
}
}
function isEnabled(key) {
return !!featureCache[key]; // Default to false if missing
}
async function setFlag(pool, key, enabled, updatedBy) {
await pool.query(
`INSERT INTO feature_flags (key, is_enabled, updated_by) VALUES ($1, $2, $3)
ON CONFLICT (key) DO UPDATE SET is_enabled = EXCLUDED.is_enabled, updated_by = EXCLUDED.updated_by`,
[key, enabled, updatedBy]
);
await refreshCache(pool);
}
module.exports = { refreshCache, isEnabled, setFlag };
```
### `src/core/features/middleware.js`
```javascript
const { isEnabled } = require('./index');
function requireFeature(featureKey) {
return (req, res, next) => {
if (isEnabled(featureKey)) {
return next();
}
const isHtmx = req.headers['hx-request'] === 'true';
const msg = `Feature '${featureKey}' is currently disabled.`;
if (isHtmx) {
return res.status(503).send(`
<div class="p-4 bg-yellow-900 border border-yellow-500 text-white rounded">
<strong>Service Unavailable:</strong> ${msg}
</div>
`);
} else {
return res.status(503).send(msg); // Adjust to your 503 template
}
};
}
module.exports = { requireFeature };
```
### `src/core/features/routes.js`
```javascript
const express = require('express');
const router = express.Router();
const db = require('../../db');
const { requirePermission } = require('../permissions/middleware');
const { setFlag } = require('./index');
router.use(requirePermission('system.features.manage'));
router.get('/api/features', async (req, res) => {
const { rows } = await db.query('SELECT * FROM feature_flags ORDER BY key');
res.json(rows);
});
router.post('/api/features/:key', async (req, res) => {
const { is_enabled } = req.body;
await setFlag(db, req.params.key, is_enabled, req.user.id);
res.sendStatus(200);
});
module.exports = router;
```
---
## 5. Boot Integration (`src/index.js`)
### `src/index.js` *(Snippet showing integration with existing Express setup)*
```javascript
const express = require('express');
const session = require('express-session');
const pgSession = require('connect-pg-simple')(session);
const db = require('./db'); // Your existing PG pool
const { bootSystem } = require('./core/boot');
const featureRoutes = require('./core/features/routes');
const rbacRoutes = require('./core/permissions/routes');
const app = express();
// Existing Middlewares (Body parser, Session, Passport, etc.)
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// ... session setup ...
// Initialize Trinity Core Architecture
(async () => {
try {
await bootSystem(app, db);
// Mount core API routes
app.use('/core', featureRoutes);
app.use('/core', rbacRoutes);
// Start listening only after boot is complete
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`[Arbiter] Trinity Console 2.0 running on port ${PORT}`);
});
} catch (err) {
console.error('[Arbiter] FAILED TO START:', err);
process.exit(1);
}
})();
```
---
## 6. Complete Example Module (Dashboard)
### `src/modules/dashboard/module.json`
```json
{
"id": "dashboard",
"name": "Dashboard",
"description": "System overview and quick stats",
"version": "1.0.0",
"author": "Trinity",
"dependencies": {},
"icon": "home",
"nav": {
"section": "General",
"position": 1
},
"permissions": [
{
"key": "dashboard.view",
"name": "View Dashboard",
"description": "Access the main overview screen"
}
],
"routes": "/dashboard"
}
```
### `src/modules/dashboard/routes.js`
```javascript
const express = require('express');
const router = express.Router();
const { requirePermission } = require('../../core/permissions/middleware');
// Protect entire module
router.use(requirePermission('dashboard.view'));
router.get('/', (req, res) => {
// res.render path defaults relative to your app's view engine setup.
// You may need to adjust the path based on your exact EJS setup.
res.render('../src/modules/dashboard/views/index', { user: req.user });
});
module.exports = router;
```
### `src/modules/dashboard/views/index.ejs`
```html
<div class="p-6 bg-slate-900 text-white min-h-screen">
<h1 class="text-3xl font-bold mb-4 text-blue-400">Welcome back, <%= user.username %></h1>
<p class="text-slate-300">The Frostwall holds strong.</p>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-8">
<div class="bg-slate-800 p-4 border border-slate-700 rounded-lg">
<h2 class="text-xl font-bold">Servers Online</h2>
<p class="text-3xl text-emerald-400 mt-2">12 / 12</p>
</div>
</div>
</div>
```
### `src/modules/dashboard/events.js`
```javascript
module.exports = function(coreEvents) {
coreEvents.on('server.status_change', (data) => {
console.log(`[Dashboard Module] Noticed server ${data.serverId} changed to ${data.status}`);
// Could update a cache here or emit over WebSockets to UI
});
};
```
### `src/modules/dashboard/api.js`
```javascript
// Functions exposed for other modules to use via registry.getModule('dashboard').api
module.exports = {
getSystemHealthScore: async () => {
return 100; // Placeholder logic
}
};
```
### `src/modules/dashboard/migrations/001_dashboard_stats.sql`
```sql
-- Placeholder migration to prove the engine works
CREATE TABLE IF NOT EXISTS dashboard_daily_stats (
date DATE PRIMARY KEY,
total_logins INT DEFAULT 0
);
```
---
## File Structure Summary
```
/opt/arbiter-3.0/
├── migrations/
│ ├── 001_rbac_tables.sql
│ └── 002_feature_flags.sql
├── src/
│ ├── core/
│ │ ├── boot.js
│ │ ├── database/
│ │ │ └── migrations.js
│ │ ├── events/
│ │ │ └── index.js
│ │ ├── features/
│ │ │ ├── index.js
│ │ │ ├── middleware.js
│ │ │ └── routes.js
│ │ ├── modules/
│ │ │ ├── loader.js
│ │ │ ├── registry.js
│ │ │ └── routes.js
│ │ ├── navigation/
│ │ │ └── index.js
│ │ └── permissions/
│ │ ├── middleware.js
│ │ ├── resolver.js
│ │ ├── routes.js
│ │ └── sync.js
│ ├── modules/
│ │ └── dashboard/
│ │ ├── module.json
│ │ ├── routes.js
│ │ ├── views/
│ │ │ └── index.ejs
│ │ ├── events.js
│ │ ├── migrations/
│ │ │ └── 001_dashboard_stats.sql
│ │ └── api.js
│ ├── db.js (existing)
│ └── index.js (updated)
```
---
*Response received April 5, 2026*