@hivemagnus/magnus-server 1.1.8 → 1.1.10

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.
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.1.8"
2
+ "version": "1.1.10"
3
3
  }
@@ -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' } } },
@@ -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: { target: target ?? null } });
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hivemagnus/magnus-server",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
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.8",
87
+ "@hivemagnus/magnus-contracts": "1.1.10",
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.8",
105
+ "@hivemagnus/magnus-skills": "1.1.10",
106
106
  "prisma": "^7.10.1"
107
107
  },
108
108
  "devDependencies": {