/** * Permission Engine * Based on: docs/foundation/Agent_Governance_Protocol_v1.md * * Implements permission checks for all governance actions */ import { db } from '../../infra/db/client'; import { logger } from '../../infra/logger/logger'; import { AgentGovLevel, AgentStatus, GovernancePower, GovernanceScope, GovernanceContext, CanDoResult, GOV_LEVEL_TO_NUM, POWER_MATRIX, TargetType, PermissionAction, } from '../../domain/governance/types'; // City governance agent IDs const CITY_AGENTS = ['daarwizz', 'dario', 'daria']; /** * Get agent's governance level */ export async function getAgentLevel(agentId: string): Promise { const result = await db.query<{ gov_level: AgentGovLevel; status: AgentStatus }>( `SELECT gov_level, status FROM agents WHERE id = $1`, [agentId] ); if (result.rows.length === 0) { return 'guest'; } if (result.rows[0].status === 'revoked') { return 'guest'; } return result.rows[0].gov_level || 'personal'; } /** * Get powers for a governance level */ export function getPowersForLevel(level: AgentGovLevel): GovernancePower[] { const entry = POWER_MATRIX.find(p => p.level === level); return entry?.powers || []; } /** * Build governance context for an actor */ export async function buildContext( actorAgentId: string, scope: GovernanceScope ): Promise { const level = await getAgentLevel(actorAgentId); const powers = getPowersForLevel(level); // Get DAIS ID for actor const agent = await db.query<{ dais_identity_id: string }>( `SELECT dais_identity_id FROM agents WHERE id = $1`, [actorAgentId] ); const daisId = agent.rows[0]?.dais_identity_id || actorAgentId; // Parse scope let microdaoId: string | undefined; let districtId: string | undefined; if (scope.startsWith('microdao:')) { microdaoId = scope.replace('microdao:', ''); } else if (scope.startsWith('district:')) { districtId = scope.replace('district:', ''); } return { actorDaisId: daisId, actorAgentId, actorLevel: level, actorPowers: powers, currentScope: scope, microdaoId, districtId, }; } // ============================================================================ // PERMISSION CHECKS // ============================================================================ /** * Check if agent can create a MicroDAO * Only Orchestrator (Level 5+) with verified DAIS */ export async function canCreateMicrodao( context: GovernanceContext ): Promise { const levelNum = GOV_LEVEL_TO_NUM[context.actorLevel]; if (levelNum < 5) { return { allowed: false, reason: 'Only Orchestrators (Level 5+) can create MicroDAO', requiredLevel: 'orchestrator', }; } // Check DAIS trust level const dais = await db.query<{ trust_level: string }>( `SELECT trust_level FROM dais_identities WHERE id = $1`, [context.actorDaisId] ); if (dais.rows.length === 0 || !['orchestrator', 'operator'].includes(dais.rows[0].trust_level)) { return { allowed: false, reason: 'DAIS must have orchestrator or operator trust level', }; } return { allowed: true }; } /** * Check if agent can create a District * Only City Governance (Level 7) or approved Orchestrator */ export async function canCreateDistrict( context: GovernanceContext ): Promise { const levelNum = GOV_LEVEL_TO_NUM[context.actorLevel]; // City governance can always create if (levelNum === 7) { return { allowed: true }; } // Orchestrator needs city approval if (levelNum >= 5) { // Check if there's a pending/approved district request const approval = await db.query( `SELECT id FROM event_outbox WHERE event_type = 'district.creation_approved' AND payload->>'requestedBy' = $1 AND status = 'published' LIMIT 1`, [context.actorAgentId] ); if (approval.rows.length > 0) { return { allowed: true }; } return { allowed: false, reason: 'Orchestrator needs city approval to create District', }; } return { allowed: false, reason: 'Only City Governance can create Districts', requiredLevel: 'city_governance', }; } /** * Check if agent can register a node * Orchestrator, Core-team DevOps, Node Manager, City Infrastructure */ export async function canRegisterNode( context: GovernanceContext ): Promise { const levelNum = GOV_LEVEL_TO_NUM[context.actorLevel]; // Core-team and above can register if (levelNum >= 4) { return { allowed: true }; } // Check for Node Manager role in assignments const assignment = await db.query( `SELECT id FROM agent_assignments WHERE agent_id = $1 AND role IN ('devops', 'node-manager') AND end_ts IS NULL`, [context.actorAgentId] ); if (assignment.rows.length > 0) { return { allowed: true }; } return { allowed: false, reason: 'Only Core-team, DevOps, or Node Managers can register nodes', requiredLevel: 'core_team', requiredPower: 'infrastructure', }; } /** * Check if agent can create a room */ export async function canCreateRoom( context: GovernanceContext, roomType: string ): Promise { const levelNum = GOV_LEVEL_TO_NUM[context.actorLevel]; switch (roomType) { case 'personal': // Personal agents can create personal rooms if (levelNum >= 1) return { allowed: true }; break; case 'project': // Workers can create project rooms if (levelNum >= 3) return { allowed: true }; break; case 'dao-room': case 'dao-wide': // Core-team can create DAO-wide rooms if (levelNum >= 4) return { allowed: true }; break; case 'front-room': case 'portal': // Orchestrator can create front-rooms and portals if (levelNum >= 5) return { allowed: true }; break; case 'city-room': // City agents only if (levelNum === 7) return { allowed: true }; break; case 'district-room': // District lead or higher if (levelNum >= 6) return { allowed: true }; break; } return { allowed: false, reason: `Insufficient permissions to create ${roomType} room`, }; } /** * Check if agent can create a front-portal in city */ export async function canCreateFrontPortal( context: GovernanceContext, targetMicrodaoId: string ): Promise { const levelNum = GOV_LEVEL_TO_NUM[context.actorLevel]; // City agents can create any portal if (levelNum === 7) { return { allowed: true }; } // District lead can create portals for their district if (levelNum === 6) { // Check if target is in their district const district = await db.query( `SELECT id FROM microdaos WHERE id = $1 AND parent_microdao_id = ( SELECT id FROM microdaos WHERE primary_orchestrator_agent_id = $2 AND dao_type = 'district' )`, [targetMicrodaoId, context.actorAgentId] ); if (district.rows.length > 0) { return { allowed: true }; } } // Orchestrator can create portal for their own MicroDAO if (levelNum === 5) { const microdao = await db.query( `SELECT id FROM microdaos WHERE id = $1 AND primary_orchestrator_agent_id = $2`, [targetMicrodaoId, context.actorAgentId] ); if (microdao.rows.length > 0) { return { allowed: true }; } return { allowed: false, reason: 'Orchestrator can only create portal for their own MicroDAO', }; } return { allowed: false, reason: 'Only Orchestrators and above can create front-portals', requiredLevel: 'orchestrator', }; } /** * Check if actor can promote target agent */ export async function canPromoteAgent( context: GovernanceContext, targetId: string, newLevel: AgentGovLevel ): Promise { const actorLevelNum = GOV_LEVEL_TO_NUM[context.actorLevel]; const newLevelNum = GOV_LEVEL_TO_NUM[newLevel]; const targetLevel = await getAgentLevel(targetId); const targetLevelNum = GOV_LEVEL_TO_NUM[targetLevel]; // Cannot promote to same or higher level than self if (newLevelNum >= actorLevelNum) { return { allowed: false, reason: 'Cannot promote agent to same or higher level than yourself', }; } // Cannot promote agent already at higher level if (targetLevelNum >= newLevelNum) { return { allowed: false, reason: 'Target agent is already at this level or higher', }; } // Only core-team and above can promote if (actorLevelNum < 4) { return { allowed: false, reason: 'Only Core-team and above can promote agents', requiredLevel: 'core_team', }; } // Check scope - can only promote in own DAO/District if (context.currentScope.startsWith('microdao:')) { const microdaoId = context.currentScope.replace('microdao:', ''); // Check if actor is orchestrator of this DAO const isOrchestrator = await db.query( `SELECT id FROM microdaos WHERE id = $1 AND primary_orchestrator_agent_id = $2`, [microdaoId, context.actorAgentId] ); // Check if target is in this DAO const targetInDao = await db.query( `SELECT id FROM agent_assignments WHERE agent_id = $1 AND target_microdao_id = $2 AND end_ts IS NULL`, [targetId, microdaoId] ); if (isOrchestrator.rows.length === 0 && actorLevelNum < 6) { return { allowed: false, reason: 'Only the DAO Orchestrator can promote agents in this DAO', }; } if (targetInDao.rows.length === 0 && actorLevelNum < 7) { return { allowed: false, reason: 'Target agent is not a member of this DAO', }; } } return { allowed: true }; } /** * Check if actor can revoke target agent */ export async function canRevokeAgent( context: GovernanceContext, targetId: string ): Promise { const actorLevelNum = GOV_LEVEL_TO_NUM[context.actorLevel]; const targetLevel = await getAgentLevel(targetId); const targetLevelNum = GOV_LEVEL_TO_NUM[targetLevel]; // Cannot revoke same or higher level if (targetLevelNum >= actorLevelNum) { return { allowed: false, reason: 'Cannot revoke agent at same or higher level', }; } // Must have identity power if (!context.actorPowers.includes('identity')) { return { allowed: false, reason: 'Requires identity power to revoke agents', requiredPower: 'identity', }; } // City governance can revoke anyone if (actorLevelNum === 7) { return { allowed: true }; } // District lead can revoke in their district if (actorLevelNum === 6) { // Check if target is in actor's district const inDistrict = await db.query( `SELECT a.id FROM agents a JOIN agent_assignments aa ON a.id = aa.agent_id JOIN microdaos m ON aa.target_microdao_id = m.id WHERE a.id = $1 AND m.parent_microdao_id = ( SELECT id FROM microdaos WHERE primary_orchestrator_agent_id = $2 AND dao_type = 'district' )`, [targetId, context.actorAgentId] ); if (inDistrict.rows.length > 0) { return { allowed: true }; } } // Orchestrator can revoke in their DAO if (actorLevelNum === 5 && context.currentScope.startsWith('microdao:')) { const microdaoId = context.currentScope.replace('microdao:', ''); const isOrchestrator = await db.query( `SELECT id FROM microdaos WHERE id = $1 AND primary_orchestrator_agent_id = $2`, [microdaoId, context.actorAgentId] ); const targetInDao = await db.query( `SELECT id FROM agent_assignments WHERE agent_id = $1 AND target_microdao_id = $2 AND end_ts IS NULL`, [targetId, microdaoId] ); if (isOrchestrator.rows.length > 0 && targetInDao.rows.length > 0) { return { allowed: true }; } } return { allowed: false, reason: 'Insufficient permissions to revoke this agent', }; } /** * Check if actor can moderate a room */ export async function canModerateRoom( context: GovernanceContext, roomId: string ): Promise { const levelNum = GOV_LEVEL_TO_NUM[context.actorLevel]; // Must have moderation power if (!context.actorPowers.includes('moderation')) { return { allowed: false, reason: 'Requires moderation power', requiredPower: 'moderation', }; } // Get room info const room = await db.query<{ owner_type: string; owner_id: string; type: string; space_scope: string; }>( `SELECT owner_type, owner_id, type, space_scope FROM rooms WHERE id = $1`, [roomId] ); if (room.rows.length === 0) { return { allowed: false, reason: 'Room not found' }; } const roomData = room.rows[0]; // City governance can moderate any room if (levelNum === 7) { return { allowed: true }; } // City rooms - only city agents if (roomData.type === 'city-room') { return { allowed: false, reason: 'Only City Governance can moderate city rooms', requiredLevel: 'city_governance', }; } // District rooms - district lead or higher if (roomData.type === 'district-room') { if (levelNum >= 6) { // Check if actor is lead of this district const isLead = await db.query( `SELECT id FROM microdaos WHERE id = $1 AND primary_orchestrator_agent_id = $2 AND dao_type = 'district'`, [roomData.owner_id, context.actorAgentId] ); if (isLead.rows.length > 0) { return { allowed: true }; } } return { allowed: false, reason: 'Only District Lead can moderate this room', requiredLevel: 'district_lead', }; } // DAO rooms - check if actor has role in this DAO if (roomData.space_scope === 'microdao') { const assignment = await db.query( `SELECT role FROM agent_assignments WHERE agent_id = $1 AND target_microdao_id = $2 AND end_ts IS NULL`, [context.actorAgentId, roomData.owner_id] ); if (assignment.rows.length > 0) { return { allowed: true }; } return { allowed: false, reason: 'Must be a member of this DAO to moderate its rooms', }; } return { allowed: true }; } /** * Check explicit permission in database */ export async function hasExplicitPermission( daisId: string, targetType: TargetType, targetId: string, action: PermissionAction ): Promise { const result = await db.query( `SELECT id FROM permissions WHERE dais_id = $1 AND target_type = $2 AND target_id = $3 AND action = $4 AND (expires_at IS NULL OR expires_at > now())`, [daisId, targetType, targetId, action] ); return result.rows.length > 0; } /** * Check if agent is a city governance agent */ export function isCityAgent(agentId: string): boolean { return CITY_AGENTS.includes(agentId); } /** * Log permission check for audit */ export async function logPermissionCheck( actorId: string, action: string, targetId: string, result: CanDoResult ): Promise { logger.info(`Permission check: ${actorId} → ${action} → ${targetId}: ${result.allowed ? 'ALLOWED' : 'DENIED'}`, { actorId, action, targetId, allowed: result.allowed, reason: result.reason, }); } export const permissionEngine = { getAgentLevel, getPowersForLevel, buildContext, canCreateMicrodao, canCreateDistrict, canRegisterNode, canCreateRoom, canCreateFrontPortal, canPromoteAgent, canRevokeAgent, canModerateRoom, hasExplicitPermission, isCityAgent, logPermissionCheck, };