Define tenant apply preflight policy
Turn tenant-apply into a structured preflight contract that marks what already passes in the declarative model, what remains manual, and what still blocks any future automatic host mutation. --- Build: pass | Tests: pass — 31 passed (1 file)
This commit is contained in:
parent
e1c969e3d0
commit
2d3f2253c9
5 changed files with 90 additions and 0 deletions
|
|
@ -82,6 +82,19 @@ Each tenant owns:
|
|||
- startup reports
|
||||
- tenant-specific worker execution
|
||||
|
||||
Tenant ownership in V2 is still declarative first. A declared tenant means:
|
||||
|
||||
- named tenant-owned databases
|
||||
- named tenant-owned worker jails
|
||||
- named tenant-owned datasets
|
||||
|
||||
It does not mean:
|
||||
|
||||
- a per-tenant hostd
|
||||
- a per-tenant Unix user
|
||||
- a per-tenant repo checkout
|
||||
- automatic live provisioning on declaration alone
|
||||
|
||||
Examples:
|
||||
|
||||
- `mevy`
|
||||
|
|
@ -258,3 +271,20 @@ V2 must discover or declare ownership instead of guessing from one name.
|
|||
4. Refactor setup and diagnostics to use the registry
|
||||
5. Make hostd authorization tenant-aware
|
||||
6. Add degraded-mode boundaries between chat, memory, and ops
|
||||
|
||||
## Future tenant apply policy
|
||||
|
||||
Any future live `tenant-apply` must satisfy these rules:
|
||||
|
||||
- create only tenant-owned resources
|
||||
- never create, mutate, or delete shared platform resources
|
||||
- remain non-destructive by default
|
||||
- require explicit operator review before host mutation
|
||||
- keep per-tenant hostd, Unix users, and repo workspaces out of scope
|
||||
|
||||
Preflight for a future live apply should at minimum verify:
|
||||
|
||||
- tenant exists in the registry
|
||||
- tenant resources are still disjoint from shared platform resources
|
||||
- tenant resources are still disjoint from other tenants
|
||||
- no blocked manual step is being silently skipped
|
||||
|
|
|
|||
|
|
@ -198,6 +198,8 @@ Also updated:
|
|||
- what a future live apply would be allowed to touch
|
||||
- prerequisites before any host mutation could exist
|
||||
- what remains manual or explicitly out of scope
|
||||
- a structured preflight checklist showing what already passes in the
|
||||
declarative model and what still blocks any automatic apply
|
||||
|
||||
## Recommended first code tasks
|
||||
|
||||
|
|
|
|||
|
|
@ -161,6 +161,9 @@ function printApplyPlan(
|
|||
): void {
|
||||
console.log(`Apply plan for tenant ${plan.tenant.id}:`);
|
||||
console.log(`Display: ${plan.tenant.displayName}`);
|
||||
console.log(
|
||||
`Automatic apply: ${plan.readyForAutomaticApply ? 'ready' : 'not ready'}`,
|
||||
);
|
||||
console.log(`Allowed databases: ${plan.allowedResources.databases.join(', ')}`);
|
||||
console.log(`Allowed worker jails: ${plan.allowedResources.workerJails.join(', ')}`);
|
||||
console.log(`Allowed datasets: ${plan.allowedResources.datasets.join(', ') || '(none)'}`);
|
||||
|
|
@ -170,6 +173,10 @@ function printApplyPlan(
|
|||
console.log(`- ${blocker}`);
|
||||
}
|
||||
}
|
||||
console.log('Preflight:');
|
||||
for (const check of plan.preflightChecks) {
|
||||
console.log(`- [${check.status}] ${check.name}: ${check.detail}`);
|
||||
}
|
||||
console.log('Prerequisites:');
|
||||
for (const item of plan.prerequisites) {
|
||||
console.log(`- ${item}`);
|
||||
|
|
|
|||
|
|
@ -82,8 +82,21 @@ describe('tenant-registry', () => {
|
|||
const registryPath = makeTempRegistry();
|
||||
const plan = planTenantApply('mevy', registryPath);
|
||||
expect(plan.tenant.id).toBe('mevy');
|
||||
expect(plan.readyForAutomaticApply).toBe(false);
|
||||
expect(plan.allowedResources.databases).toContain('mevy_brain');
|
||||
expect(plan.allowedResources.workerJails).toContain('mevy_ctrl_worker');
|
||||
expect(plan.preflightChecks).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: 'Registry declaration',
|
||||
status: 'pass',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'Live apply implementation',
|
||||
status: 'blocked',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
expect(plan.manualSteps).toContain(
|
||||
'No live resources are created by this workflow today.',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -138,11 +138,17 @@ export interface TenantProvisioningPlan {
|
|||
export interface TenantApplyPlan {
|
||||
tenant: TenantRecord;
|
||||
registryPath: string;
|
||||
readyForAutomaticApply: boolean;
|
||||
allowedResources: {
|
||||
databases: string[];
|
||||
workerJails: string[];
|
||||
datasets: string[];
|
||||
};
|
||||
preflightChecks: Array<{
|
||||
name: string;
|
||||
status: 'pass' | 'manual' | 'blocked';
|
||||
detail: string;
|
||||
}>;
|
||||
prerequisites: string[];
|
||||
manualSteps: string[];
|
||||
blockers: string[];
|
||||
|
|
@ -675,11 +681,43 @@ export function planTenantApply(
|
|||
return {
|
||||
tenant,
|
||||
registryPath,
|
||||
readyForAutomaticApply: false,
|
||||
allowedResources: {
|
||||
databases: Object.values(tenant.databases),
|
||||
workerJails: tenant.workerJails,
|
||||
datasets: tenant.datasets,
|
||||
},
|
||||
preflightChecks: [
|
||||
{
|
||||
name: 'Registry declaration',
|
||||
status: 'pass',
|
||||
detail: 'Tenant already exists in the registry.',
|
||||
},
|
||||
{
|
||||
name: 'Ownership disjointness',
|
||||
status: 'pass',
|
||||
detail:
|
||||
'Tenant resources remain disjoint from shared platform resources and other tenants by registry validation.',
|
||||
},
|
||||
{
|
||||
name: 'Shared-resource exclusion',
|
||||
status: 'pass',
|
||||
detail:
|
||||
'Platform-owned shared services, datasets, and jails stay outside tenant apply.',
|
||||
},
|
||||
{
|
||||
name: 'Live apply implementation',
|
||||
status: 'blocked',
|
||||
detail:
|
||||
'No host-mutating tenant apply workflow exists yet; this command is planning only.',
|
||||
},
|
||||
{
|
||||
name: 'Operator confirmation',
|
||||
status: 'manual',
|
||||
detail:
|
||||
'Any future live apply should require explicit operator review before touching host state.',
|
||||
},
|
||||
],
|
||||
prerequisites: [
|
||||
'Tenant must already exist in the registry.',
|
||||
'Platform-owned shared services must already remain outside tenant apply.',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue