@hivemagnus/magnus-server 1.1.8 → 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/agent-version.json
CHANGED
package/dist/openapi/spec.js
CHANGED
|
@@ -2571,12 +2571,24 @@ const paths = {
|
|
|
2571
2571
|
responses: { 200: { description: 'Artifact bytes', content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } },
|
|
2572
2572
|
401: err401, 403: err403, 404: err404, 503: { description: 'Node or profile API unavailable' } } },
|
|
2573
2573
|
},
|
|
2574
|
+
'/ardent/installations/{nodeId}/profiles/{name}/context-release': {
|
|
2575
|
+
get: { tags: ['Magnus Ardent'], summary: 'Inspect the pinned context-only adoption payload without changing a profile',
|
|
2576
|
+
security: apiSecretAuth, parameters: [pp('nodeId', 'Managed node ID'), pp('name', 'Profile name')],
|
|
2577
|
+
responses: { 200: success('Context release digests and service compatibility'), 401: err401, 403: err403, 503: { description: 'Context release unavailable' } } },
|
|
2578
|
+
},
|
|
2579
|
+
'/ardent/installations/{nodeId}/profiles/{name}/context-adoptions/{releaseId}': {
|
|
2580
|
+
get: { tags: ['Magnus Ardent'], summary: 'Inspect a retained context-adoption journal without retrying work',
|
|
2581
|
+
description: 'A journal phase is diagnostic state, not a signed completion receipt or permission to clear an unsettled resource lease.',
|
|
2582
|
+
security: apiSecretAuth, parameters: [pp('nodeId', 'Managed node ID'), pp('name', 'Profile name'), pp('releaseId', 'Adoption release UUID')],
|
|
2583
|
+
responses: { 200: success('Retained revision pins, acceptance and phase'), 400: err400, 401: err401, 403: err403,
|
|
2584
|
+
503: { description: 'Retained journal unavailable' } } },
|
|
2585
|
+
},
|
|
2574
2586
|
'/ardent/installations/{nodeId}/profiles/{name}/action': {
|
|
2575
2587
|
post: { tags: ['Magnus Ardent'], summary: 'Run a finite node-scoped Ardent profile action',
|
|
2576
|
-
description: 'The Control API dispatches through the node managed-mutation boundary. A task-submit result proves durable admission only; task completion is read from the Ardent task API.',
|
|
2588
|
+
description: 'The Control API dispatches through the node managed-mutation boundary. A task-submit result proves durable admission only; task completion is read from the Ardent task API. Context-stage, context-adopt and context-rollback require a release UUID and exact bundle, base-runtime, policy and configuration SHA-256 pins in body, with no target. Context actions require a signed managed owner and return verified completion evidence. A timeout or failure after dispatch must be inspected, never automatically retried.',
|
|
2577
2589
|
security: apiSecretAuth, parameters: [pp('nodeId', 'Managed node ID'), pp('name', 'Profile name')],
|
|
2578
2590
|
requestBody: { required: true, content: { 'application/json': { schema: { type: 'object', required: ['action'],
|
|
2579
|
-
properties: { action: { type: 'string', enum: ['profile-create', 'config-validate', 'config-save', 'peer-validate', 'peer-save', 'identity-save', 'identity-restore', 'provider-test', 'channel-test', 'peer-test', 'memory-search', 'task-submit', 'task-cancel', 'skill-set', 'service-start', 'service-stop', 'service-restart', 'package-install'] },
|
|
2591
|
+
properties: { action: { type: 'string', enum: ['profile-create', 'config-validate', 'config-save', 'peer-validate', 'peer-save', 'identity-save', 'identity-restore', 'provider-test', 'channel-test', 'peer-test', 'memory-search', 'task-submit', 'task-cancel', 'skill-set', 'service-start', 'service-stop', 'service-restart', 'package-install', 'context-stage', 'context-adopt', 'context-rollback'] },
|
|
2580
2592
|
target: { type: 'string' }, body: {} }, additionalProperties: false } } } },
|
|
2581
2593
|
responses: { 200: success('Ardent action acknowledgement'), 400: err400, 401: err401, 403: err403,
|
|
2582
2594
|
503: { description: 'Node or Ardent action unavailable' } } },
|
package/dist/routes/ardent.js
CHANGED
|
@@ -2,8 +2,9 @@ import { Hono } from 'hono';
|
|
|
2
2
|
import { authMiddleware, getUser, requireRole } from '../middleware/auth.js';
|
|
3
3
|
import { HttpError } from '../middleware/error-handler.js';
|
|
4
4
|
import { logAuditEvent } from '../services/audit-service.js';
|
|
5
|
-
import { getArdentArtifact, getArdentInstallation, getArdentProfileResource, listArdentInstallations, performArdentAction } from '../services/ardent-service.js';
|
|
5
|
+
import { getArdentArtifact, getArdentContextAdoption, getArdentContextRelease, getArdentInstallation, getArdentProfileResource, listArdentInstallations, performArdentAction } from '../services/ardent-service.js';
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
+
import { ArdentContextAdoptionInputSchema } from '@hivemagnus/magnus-contracts';
|
|
7
8
|
export const ardentRouter = new Hono();
|
|
8
9
|
ardentRouter.use('*', authMiddleware);
|
|
9
10
|
ardentRouter.use('*', requireRole('ADMIN'));
|
|
@@ -14,6 +15,15 @@ const id = (value) => {
|
|
|
14
15
|
};
|
|
15
16
|
ardentRouter.get('/installations', async (c) => c.json({ success: true, data: await listArdentInstallations() }));
|
|
16
17
|
ardentRouter.get('/installations/:nodeId', async (c) => c.json({ success: true, data: await getArdentInstallation(id(c.req.param('nodeId'))) }));
|
|
18
|
+
ardentRouter.get('/installations/:nodeId/profiles/:name/context-release', async (c) => {
|
|
19
|
+
return c.json({ success: true, data: await getArdentContextRelease(id(c.req.param('nodeId')), id(c.req.param('name'))) });
|
|
20
|
+
});
|
|
21
|
+
ardentRouter.get('/installations/:nodeId/profiles/:name/context-adoptions/:releaseId', async (c) => {
|
|
22
|
+
const releaseId = z.string().guid().safeParse(c.req.param('releaseId'));
|
|
23
|
+
if (!releaseId.success)
|
|
24
|
+
throw new HttpError(400, 'VALIDATION_ERROR', 'Invalid context adoption release ID');
|
|
25
|
+
return c.json({ success: true, data: await getArdentContextAdoption(id(c.req.param('nodeId')), id(c.req.param('name')), releaseId.data) });
|
|
26
|
+
});
|
|
17
27
|
ardentRouter.get('/profiles', async (c) => {
|
|
18
28
|
const installations = await listArdentInstallations();
|
|
19
29
|
return c.json({ success: true, data: installations.flatMap(installation => installation.profiles.map(profile => ({ ...profile,
|
|
@@ -59,7 +69,7 @@ ardentRouter.get('/installations/:nodeId/profiles/:name/*', async (c) => {
|
|
|
59
69
|
}
|
|
60
70
|
});
|
|
61
71
|
const Action = z.object({
|
|
62
|
-
action: z.enum(['profile-create', 'profile-import-hermes', 'config-validate', 'config-save', 'peer-validate', 'peer-save', 'identity-save', 'identity-restore', 'provider-test', 'channel-test', 'peer-test', 'memory-search', 'task-submit', 'task-cancel', 'skill-set', 'service-start', 'service-stop', 'service-restart', 'package-install']),
|
|
72
|
+
action: z.enum(['profile-create', 'profile-import-hermes', 'config-validate', 'config-save', 'peer-validate', 'peer-save', 'identity-save', 'identity-restore', 'provider-test', 'channel-test', 'peer-test', 'memory-search', 'task-submit', 'task-cancel', 'skill-set', 'service-start', 'service-stop', 'service-restart', 'package-install', 'context-stage', 'context-adopt', 'context-rollback']),
|
|
63
73
|
target: z.string().min(1).max(128).optional(), body: z.unknown().optional(),
|
|
64
74
|
}).strict();
|
|
65
75
|
ardentRouter.post('/installations/:nodeId/profiles/:name/action', async (c) => {
|
|
@@ -69,10 +79,16 @@ ardentRouter.post('/installations/:nodeId/profiles/:name/action', async (c) => {
|
|
|
69
79
|
if (!parsed.success)
|
|
70
80
|
throw new HttpError(400, 'VALIDATION_ERROR', 'Invalid Ardent action');
|
|
71
81
|
const { action, target, body } = parsed.data;
|
|
82
|
+
if (['context-stage', 'context-adopt', 'context-rollback'].includes(action)
|
|
83
|
+
&& (target !== undefined || !ArdentContextAdoptionInputSchema.safeParse(body).success))
|
|
84
|
+
throw new HttpError(400, 'VALIDATION_ERROR', 'Context adoption requires exact release, source and policy pins without an arbitrary target');
|
|
72
85
|
try {
|
|
73
86
|
const result = await performArdentAction(nodeId, profile, action, target, body);
|
|
74
87
|
await logAuditEvent({ action: `ardent.${action}`, actorId: getUser(c).userId,
|
|
75
|
-
targetType: 'ardent-profile', targetId: `${nodeId}:${profile}`, details: {
|
|
88
|
+
targetType: 'ardent-profile', targetId: `${nodeId}:${profile}`, details: {
|
|
89
|
+
target: target ?? null,
|
|
90
|
+
...(['context-stage', 'context-adopt', 'context-rollback'].includes(action) ? ArdentContextAdoptionInputSchema.parse(body) : {})
|
|
91
|
+
} });
|
|
76
92
|
return c.json({ success: true, data: result });
|
|
77
93
|
}
|
|
78
94
|
catch (error) {
|
|
@@ -11,7 +11,7 @@ import ky, { HTTPError } from 'ky';
|
|
|
11
11
|
import { HttpError } from '../middleware/error-handler.js';
|
|
12
12
|
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
13
13
|
import { z } from 'zod';
|
|
14
|
-
import { agentUpgradePreflightSchema } from '@hivemagnus/magnus-contracts';
|
|
14
|
+
import { agentUpgradePreflightSchema, ArdentContextAdoptionInputSchema } from '@hivemagnus/magnus-contracts';
|
|
15
15
|
import { RESULT_SIGNING_PREVIOUS_SECRET, RESULT_SIGNING_SECRET } from '../utils/env.js';
|
|
16
16
|
import { correlateSkillPublicationNoEffect } from '../services/skill-publication-settlement.js';
|
|
17
17
|
import { commandResultSigningPayload, verifyCommandResultSignature, } from '../security/command-result-signature.js';
|
|
@@ -636,6 +636,25 @@ export class NodeApiClient {
|
|
|
636
636
|
const timeout = action === 'package-install' ? 600_000 : action === 'profile-import-hermes' ? 90_000 : action.startsWith('service-') ? 115_000 : 45_000;
|
|
637
637
|
return this.mutate('POST', 'ardent/action', { profile, action, ...(target ? { target } : {}), body: body ?? {} }, timeout);
|
|
638
638
|
}
|
|
639
|
+
async getArdentContextRelease(profile) {
|
|
640
|
+
if (!/^[A-Za-z0-9_-]{1,64}$/.test(profile))
|
|
641
|
+
throw new Error('Invalid Ardent context profile');
|
|
642
|
+
return this.api.get(`ardent/profiles/${profile}/context`, { timeout: 15000, retry: 0 }).json();
|
|
643
|
+
}
|
|
644
|
+
async getArdentContextAdoption(profile, releaseId) {
|
|
645
|
+
if (!/^[A-Za-z0-9_-]{1,64}$/.test(profile) || !/^[a-f0-9-]{36}$/i.test(releaseId))
|
|
646
|
+
throw new Error('Invalid context adoption journal target');
|
|
647
|
+
return this.api.get(`ardent/profiles/${profile}/context/${releaseId}`, { timeout: 15000, retry: 0 }).json();
|
|
648
|
+
}
|
|
649
|
+
async performArdentContextAction(profile, action, body) {
|
|
650
|
+
if (!/^[A-Za-z0-9_-]{1,64}$/.test(profile) || !['context-stage', 'context-adopt', 'context-rollback'].includes(action))
|
|
651
|
+
throw new Error('Invalid context adoption action');
|
|
652
|
+
const input = ArdentContextAdoptionInputSchema.parse(body);
|
|
653
|
+
const result = await this.mutateWithEvidence('POST', 'ardent/action', { profile, action, body: input }, 850000, undefined, undefined, true);
|
|
654
|
+
if (!result.evidence)
|
|
655
|
+
throw new Error('Context adoption requires a signed managed mutation receipt');
|
|
656
|
+
return { response: result.response, evidence: result.evidence };
|
|
657
|
+
}
|
|
639
658
|
async updateCodex() {
|
|
640
659
|
return this.mutate('POST', 'codex/update', undefined, 11 * 60_000);
|
|
641
660
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ArdentNodeStateSchema } from '@hivemagnus/magnus-contracts';
|
|
1
|
+
import { ArdentNodeStateSchema, ArdentContextReleaseSchema, ArdentContextAdoptionInputSchema } from '@hivemagnus/magnus-contracts';
|
|
2
2
|
import { getDb } from '../db/index.js';
|
|
3
3
|
import { NodeApiClient } from '../sdk/index.js';
|
|
4
4
|
import { getNodeBaseUrl } from '../utils/node-url.js';
|
|
@@ -81,6 +81,23 @@ export async function getArdentArtifact(nodeId, profile, artifactId) {
|
|
|
81
81
|
return clientFor(await nodeTarget(nodeId)).getArdentArtifact(profile, artifactId);
|
|
82
82
|
}
|
|
83
83
|
export async function performArdentAction(nodeId, profile, action, target, body) {
|
|
84
|
+
if (['context-stage', 'context-adopt', 'context-rollback'].includes(action)) {
|
|
85
|
+
if (target !== undefined)
|
|
86
|
+
throw new Error('Context adoption does not accept an arbitrary target');
|
|
87
|
+
const input = ArdentContextAdoptionInputSchema.parse(body);
|
|
88
|
+
const node = await nodeTarget(nodeId);
|
|
89
|
+
const enrollment = await getDb().resourceEnrollment.findUnique({ where: { nodeId } });
|
|
90
|
+
const client = enrollment?.state === 'enrolled'
|
|
91
|
+
? new NodeApiClient({ baseUrl: getNodeBaseUrl(node.ipAddress, node.port), resourceNodeId: node.id,
|
|
92
|
+
resourceActorId: 'system:ardent-context-adoption', nodeToken: getNodeManagerSecret() }) : clientFor(node);
|
|
93
|
+
return client.performArdentContextAction(profile, action, input);
|
|
94
|
+
}
|
|
84
95
|
return clientFor(await nodeTarget(nodeId)).performArdentAction(profile, action, target, body);
|
|
85
96
|
}
|
|
97
|
+
export async function getArdentContextRelease(nodeId, profile) {
|
|
98
|
+
return ArdentContextReleaseSchema.parse(await clientFor(await nodeTarget(nodeId)).getArdentContextRelease(profile));
|
|
99
|
+
}
|
|
100
|
+
export async function getArdentContextAdoption(nodeId, profile, releaseId) {
|
|
101
|
+
return clientFor(await nodeTarget(nodeId)).getArdentContextAdoption(profile, releaseId);
|
|
102
|
+
}
|
|
86
103
|
//# sourceMappingURL=ardent-service.js.map
|
|
@@ -17,7 +17,7 @@ import { RESULT_SIGNING_SECRET, RESULT_SIGNING_PREVIOUS_SECRET } from '../utils/
|
|
|
17
17
|
import { getNodeBaseUrl } from '../utils/node-url.js';
|
|
18
18
|
import { COMMAND_COMPLETION_REGISTRY, COMMAND_COMPLETION_REGISTRY_SHA256, commandCompletionDescriptor } from '../security/command-completion-registry.js';
|
|
19
19
|
import { MANAGED_HTTP_COMMAND } from '../security/managed-http.js';
|
|
20
|
-
import { NATIVE_COMPLETION_REGISTRY, NATIVE_COMPLETION_REGISTRY_SHA256, nativeCompletionDescriptor } from '../security/native-completion-registry.js';
|
|
20
|
+
import { NATIVE_COMPLETION_REGISTRY, NATIVE_COMPLETION_REGISTRY_SHA256, nativeCompletionDescriptor, nativeCompletionResourceKey } from '../security/native-completion-registry.js';
|
|
21
21
|
import { logAuditEvent } from './audit-service.js';
|
|
22
22
|
const HELD = ['reserved', 'running', 'unknown'];
|
|
23
23
|
export class ResourceLeaseConflict extends HttpError {
|
|
@@ -147,7 +147,9 @@ export async function admitResourceCommand(context, raw) {
|
|
|
147
147
|
}
|
|
148
148
|
const resourceKey = scopedArdent ? `node:${node.id}:ardent`
|
|
149
149
|
: serviceUnit ? `node:${node.id}:system-service:${serviceUnit}` : `node:${node.id}:all`;
|
|
150
|
-
const
|
|
150
|
+
const native = payload.command === MANAGED_HTTP_COMMAND && payload.args.length === 3 && /^[a-f0-9]{64}$/.test(payload.args[2])
|
|
151
|
+
? nativeCompletionDescriptor(payload.args[0], payload.args[1]) : undefined;
|
|
152
|
+
const coordinationKey = native ? nativeCompletionResourceKey(native, node.id) : `node:${node.id}`;
|
|
151
153
|
// The node ledger has one execution slot: service metadata never promises parallel fences.
|
|
152
154
|
const conflict = await tx.commandResourceLease.findFirst({ where: { nodeId: node.id, state: { in: HELD } } });
|
|
153
155
|
if (conflict)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hivemagnus/magnus-server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.12",
|
|
4
4
|
"description": "Magnus server: the fleet control plane API (Hono, PostgreSQL, Redis) for nodes running magnus-agent, with optional Hive IAM, Hive Router, HiveMem, Hermes Agent and external security console integrations.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"magnus",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"prepublishOnly": "npm run db:generate && npm run clean && npm run build"
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@hivemagnus/magnus-contracts": "1.1.
|
|
87
|
+
"@hivemagnus/magnus-contracts": "1.1.12",
|
|
88
88
|
"@hono/node-server": "^2.1.5",
|
|
89
89
|
"@hono/swagger-ui": "^0.6.2",
|
|
90
90
|
"@prisma/adapter-pg": "7.10.1",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"ws": "^8.22.0",
|
|
103
103
|
"yaml": "2.9.1",
|
|
104
104
|
"zod": "^4.6.7",
|
|
105
|
-
"@hivemagnus/magnus-skills": "1.1.
|
|
105
|
+
"@hivemagnus/magnus-skills": "1.1.12",
|
|
106
106
|
"prisma": "^7.10.1"
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|