@serve.zone/interfaces 30.1.0 → 30.2.0

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/changelog.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-09-09 - 30.2.0
4
+
5
+ ### Features
6
+
7
+ - add service machine credential contracts (service-credentials)
8
+ - Add service machine credential grant, metadata and mutation contracts with structural snapshot validators.
9
+ - Expose admin typed requests for inspecting metadata and creating, rotating or revoking service machine credentials.
10
+ - Document SecretSet delivery semantics and add validation coverage for credential contract boundaries.
11
+
3
12
  ## 2026-09-09 - 30.1.0
4
13
 
5
14
  ### Features
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/interfaces',
6
- version: '30.1.0',
6
+ version: '30.2.0',
7
7
  description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
@@ -34,6 +34,7 @@ export * from './backup.js';
34
34
  export * from './clusternode.js';
35
35
  export * from './settings.js';
36
36
  export * from './service.js';
37
+ export * from './servicecredential.js';
37
38
  export * from './serviceports.js';
38
39
  export * from './status.js';
39
40
  export * from './taskexecution.js';
@@ -34,6 +34,7 @@ export * from './backup.js';
34
34
  export * from './clusternode.js';
35
35
  export * from './settings.js';
36
36
  export * from './service.js';
37
+ export * from './servicecredential.js';
37
38
  export * from './serviceports.js';
38
39
  export * from './status.js';
39
40
  export * from './taskexecution.js';
@@ -41,4 +42,4 @@ export * from './traffic.js';
41
42
  export * from './user.js';
42
43
  export * from './version.js';
43
44
  export * from './webpush.js';
44
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxlQUFlLENBQUM7QUFDOUIsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMseUJBQXlCLENBQUM7QUFDeEMsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMsZ0JBQWdCLENBQUM7QUFDL0IsY0FBYyxVQUFVLENBQUM7QUFDekIsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxZQUFZLENBQUM7QUFDM0IsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLGdCQUFnQixDQUFDO0FBQy9CLGNBQWMsWUFBWSxDQUFDO0FBQzNCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLDZCQUE2QixDQUFDO0FBQzVDLGNBQWMsV0FBVyxDQUFDO0FBQzFCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxxQkFBcUIsQ0FBQztBQUNwQyxjQUFjLGVBQWUsQ0FBQztBQUM5QixjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyxxQkFBcUIsQ0FBQztBQUNwQyxjQUFjLDZCQUE2QixDQUFDO0FBQzVDLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsZ0JBQWdCLENBQUM7QUFDL0IsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxrQkFBa0IsQ0FBQztBQUNqQyxjQUFjLGVBQWUsQ0FBQztBQUM5QixjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLG1CQUFtQixDQUFDO0FBQ2xDLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxXQUFXLENBQUM7QUFDMUIsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxjQUFjLENBQUMifQ==
45
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxlQUFlLENBQUM7QUFDOUIsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMseUJBQXlCLENBQUM7QUFDeEMsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMsZ0JBQWdCLENBQUM7QUFDL0IsY0FBYyxVQUFVLENBQUM7QUFDekIsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxZQUFZLENBQUM7QUFDM0IsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLGdCQUFnQixDQUFDO0FBQy9CLGNBQWMsWUFBWSxDQUFDO0FBQzNCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLDZCQUE2QixDQUFDO0FBQzVDLGNBQWMsV0FBVyxDQUFDO0FBQzFCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxxQkFBcUIsQ0FBQztBQUNwQyxjQUFjLGVBQWUsQ0FBQztBQUM5QixjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyxxQkFBcUIsQ0FBQztBQUNwQyxjQUFjLDZCQUE2QixDQUFDO0FBQzVDLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsZ0JBQWdCLENBQUM7QUFDL0IsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxrQkFBa0IsQ0FBQztBQUNqQyxjQUFjLGVBQWUsQ0FBQztBQUM5QixjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsbUJBQW1CLENBQUM7QUFDbEMsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLFdBQVcsQ0FBQztBQUMxQixjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLGNBQWMsQ0FBQyJ9
@@ -0,0 +1,57 @@
1
+ /** Workload permissions are independent of human membership and deployment grants. */
2
+ export type TServiceMachineCapabilityV1 = 'platform:session';
3
+ /** Exact existing-service authority. Never infer this grant from an API role. */
4
+ export interface IServiceMachineGrantV1 {
5
+ organizationId: string;
6
+ serviceId: string;
7
+ capabilities: TServiceMachineCapabilityV1[];
8
+ }
9
+ /** Public metadata only. The bearer is delivered through the owning service's SecretSet. */
10
+ export interface IServiceMachineCredentialMetadataV1 {
11
+ schemaVersion: 1;
12
+ credentialId: string;
13
+ userId: string;
14
+ grant: IServiceMachineGrantV1;
15
+ generation: number;
16
+ state: 'active' | 'revoked';
17
+ expiresAt: number;
18
+ createdAt: number;
19
+ updatedAt: number;
20
+ /** Exact immutable encrypted delivery version; null after revocation. */
21
+ delivery: {
22
+ secretId: string;
23
+ secretVersionId: string;
24
+ } | null;
25
+ }
26
+ interface IServiceMachineCredentialMutationBaseV1 {
27
+ schemaVersion: 1;
28
+ organizationId: string;
29
+ serviceId: string;
30
+ /** Retain the complete request and this ID across retries. */
31
+ mutationId: string;
32
+ }
33
+ /** Creation is absent-only; rotation and revocation require the exact current generation. */
34
+ export type TServiceMachineCredentialMutationV1 = IServiceMachineCredentialMutationBaseV1 & ({
35
+ operation: 'ensure';
36
+ expectedGeneration: null;
37
+ capabilities: TServiceMachineCapabilityV1[];
38
+ expiresAt: number;
39
+ } | {
40
+ operation: 'rotate';
41
+ expectedGeneration: number;
42
+ capabilities: TServiceMachineCapabilityV1[];
43
+ expiresAt: number;
44
+ } | {
45
+ operation: 'revoke';
46
+ expectedGeneration: number;
47
+ });
48
+ export interface IServiceMachineCredentialMutationResultV1 {
49
+ /** Historical on replay; replay never restores revoked or superseded authority. */
50
+ credential: IServiceMachineCredentialMetadataV1;
51
+ replayed: boolean;
52
+ }
53
+ /** Structural copy only; the backend must fence live service ownership and credential authority. */
54
+ export declare const snapshotServiceMachineGrantV1: (value: unknown) => IServiceMachineGrantV1;
55
+ export declare const snapshotServiceMachineCredentialMutationV1: (value: unknown) => TServiceMachineCredentialMutationV1;
56
+ export declare const snapshotServiceMachineCredentialMetadataV1: (value: unknown) => IServiceMachineCredentialMetadataV1;
57
+ export {};
@@ -0,0 +1,81 @@
1
+ const fail = () => { throw new Error('Service credential contract rejected.'); };
2
+ function requireValue(value) { if (!value)
3
+ fail(); }
4
+ const object = (value, keys) => {
5
+ requireValue(value !== null && typeof value === 'object' && !Array.isArray(value));
6
+ requireValue(Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
7
+ const actual = Reflect.ownKeys(value);
8
+ requireValue(actual.length === keys.length && keys.every((key) => actual.includes(key)));
9
+ const copy = Object.create(null);
10
+ for (const key of keys) {
11
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
12
+ requireValue(descriptor && Object.hasOwn(descriptor, 'value') && descriptor.enumerable);
13
+ copy[key] = descriptor.value;
14
+ }
15
+ return copy;
16
+ };
17
+ const identifier = (value) => {
18
+ requireValue(typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(value));
19
+ return value;
20
+ };
21
+ const counter = (value) => {
22
+ requireValue(typeof value === 'number' && Number.isSafeInteger(value) && value > 0 && value < Number.MAX_SAFE_INTEGER);
23
+ return value;
24
+ };
25
+ const capabilities = (value) => {
26
+ requireValue(Array.isArray(value) && Object.getPrototypeOf(value) === Array.prototype);
27
+ const keys = Reflect.ownKeys(value);
28
+ requireValue(keys.length === 2 && keys.includes('length') && keys.includes('0'));
29
+ const entry = Object.getOwnPropertyDescriptor(value, '0');
30
+ requireValue(entry && Object.hasOwn(entry, 'value') && entry.enumerable && entry.value === 'platform:session');
31
+ return ['platform:session'];
32
+ };
33
+ const safe = (operation) => { try {
34
+ return operation();
35
+ }
36
+ catch {
37
+ return fail();
38
+ } };
39
+ /** Structural copy only; the backend must fence live service ownership and credential authority. */
40
+ export const snapshotServiceMachineGrantV1 = (value) => safe(() => {
41
+ const grant = object(value, ['organizationId', 'serviceId', 'capabilities']);
42
+ return { organizationId: identifier(grant.organizationId), serviceId: identifier(grant.serviceId),
43
+ capabilities: capabilities(grant.capabilities) };
44
+ });
45
+ export const snapshotServiceMachineCredentialMutationV1 = (value) => safe(() => {
46
+ requireValue(value !== null && typeof value === 'object');
47
+ const operation = Object.getOwnPropertyDescriptor(value, 'operation')?.value;
48
+ requireValue(operation === 'ensure' || operation === 'rotate' || operation === 'revoke');
49
+ const request = object(value, ['schemaVersion', 'organizationId', 'serviceId', 'mutationId', 'operation',
50
+ 'expectedGeneration', ...(operation === 'revoke' ? [] : ['capabilities', 'expiresAt'])]);
51
+ requireValue(request.schemaVersion === 1);
52
+ const base = { schemaVersion: 1, organizationId: identifier(request.organizationId),
53
+ serviceId: identifier(request.serviceId), mutationId: identifier(request.mutationId) };
54
+ if (operation === 'revoke')
55
+ return { ...base, operation, expectedGeneration: counter(request.expectedGeneration) };
56
+ const material = { capabilities: capabilities(request.capabilities), expiresAt: counter(request.expiresAt) };
57
+ if (operation === 'ensure') {
58
+ requireValue(request.expectedGeneration === null);
59
+ return { ...base, ...material, operation, expectedGeneration: null };
60
+ }
61
+ return { ...base, ...material, operation, expectedGeneration: counter(request.expectedGeneration) };
62
+ });
63
+ export const snapshotServiceMachineCredentialMetadataV1 = (value) => safe(() => {
64
+ const metadata = object(value, ['schemaVersion', 'credentialId', 'userId', 'grant', 'generation', 'state',
65
+ 'expiresAt', 'createdAt', 'updatedAt', 'delivery']);
66
+ requireValue(metadata.schemaVersion === 1 && (metadata.state === 'active' || metadata.state === 'revoked'));
67
+ const createdAt = counter(metadata.createdAt);
68
+ const updatedAt = counter(metadata.updatedAt);
69
+ requireValue(updatedAt >= createdAt);
70
+ let delivery = null;
71
+ if (metadata.state === 'active') {
72
+ const version = object(metadata.delivery, ['secretId', 'secretVersionId']);
73
+ delivery = { secretId: identifier(version.secretId), secretVersionId: identifier(version.secretVersionId) };
74
+ }
75
+ else
76
+ requireValue(metadata.delivery === null);
77
+ return { schemaVersion: 1, credentialId: identifier(metadata.credentialId), userId: identifier(metadata.userId),
78
+ grant: snapshotServiceMachineGrantV1(metadata.grant), generation: counter(metadata.generation),
79
+ state: metadata.state, expiresAt: counter(metadata.expiresAt), createdAt, updatedAt, delivery };
80
+ });
81
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZWNyZWRlbnRpYWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL3NlcnZpY2VjcmVkZW50aWFsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQXdEQSxNQUFNLElBQUksR0FBRyxHQUFVLEVBQUUsR0FBRyxNQUFNLElBQUksS0FBSyxDQUFDLHVDQUF1QyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDeEYsU0FBUyxZQUFZLENBQUMsS0FBYyxJQUFtQixJQUFJLENBQUMsS0FBSztJQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztBQUM1RSxNQUFNLE1BQU0sR0FBRyxDQUFDLEtBQWMsRUFBRSxJQUFjLEVBQTJCLEVBQUU7SUFDekUsWUFBWSxDQUFDLEtBQUssS0FBSyxJQUFJLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO0lBQ25GLFlBQVksQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxLQUFLLE1BQU0sQ0FBQyxTQUFTLElBQUksTUFBTSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsS0FBSyxJQUFJLENBQUMsQ0FBQztJQUN6RyxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3RDLFlBQVksQ0FBQyxNQUFNLENBQUMsTUFBTSxLQUFLLElBQUksQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDekYsTUFBTSxJQUFJLEdBQTRCLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDMUQsS0FBSyxNQUFNLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN2QixNQUFNLFVBQVUsR0FBRyxNQUFN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
@@ -1,5 +1,30 @@
1
1
  import * as plugins from '../plugins.js';
2
2
  import type { IIdentity, IIdentityCredential, IServiceDeploymentGrant } from '../data/user.js';
3
+ import type { IServiceMachineCredentialMetadataV1, IServiceMachineCredentialMutationResultV1, TServiceMachineCredentialMutationV1 } from '../data/servicecredential.js';
4
+ export interface IReq_Admin_GetServiceMachineCredentialV1 extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_GetServiceMachineCredentialV1> {
5
+ method: 'getServiceMachineCredentialV1';
6
+ request: {
7
+ identity: IIdentityCredential;
8
+ organizationId: string;
9
+ serviceId: string;
10
+ };
11
+ response: {
12
+ credential: IServiceMachineCredentialMetadataV1 | null;
13
+ };
14
+ }
15
+ /**
16
+ * The backend authorizes the exact service and mutates hashed credential authority,
17
+ * encrypted SecretSet delivery and the replay receipt in one fenced transaction.
18
+ * No bearer is returned. Existing legacy deployment/cluster credentials are separate.
19
+ */
20
+ export interface IReq_Admin_MutateServiceMachineCredentialV1 extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_MutateServiceMachineCredentialV1> {
21
+ method: 'mutateServiceMachineCredentialV1';
22
+ request: {
23
+ identity: IIdentityCredential;
24
+ mutation: TServiceMachineCredentialMutationV1;
25
+ };
26
+ response: IServiceMachineCredentialMutationResultV1;
27
+ }
3
28
  /** Backend-owned human credential metadata; never includes a password verifier. */
4
29
  export interface IHumanCredentialMetadata {
5
30
  userId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serve.zone/interfaces",
3
- "version": "30.1.0",
3
+ "version": "30.2.0",
4
4
  "private": false,
5
5
  "description": "Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.",
6
6
  "exports": {
package/readme.md CHANGED
@@ -174,6 +174,24 @@ bounded time/concurrency, root-only directory/socket permissions and drained
174
174
  cleanup. The package supplies no listener, permission checks or persistent state.
175
175
  No bearer, generic rotation operation or database access is part of this IPC API.
176
176
 
177
+ ### Service Machine Credentials
178
+
179
+ `requests.admin.getServiceMachineCredentialV1` inspects redacted metadata;
180
+ `mutateServiceMachineCredentialV1` creates, rotates or revokes authority for one
181
+ exact existing service and organization. `ensure` requires absent authority and
182
+ a null expected generation. Rotation and revocation require the exact current
183
+ generation. Retain the mutation ID and complete input for retry; a receipt replay
184
+ returns historical metadata without restoring authority.
185
+
186
+ `IServiceMachineGrantV1` currently grants only `platform:session`. This is separate
187
+ from deployment grants, human membership and infrastructure permissions. The
188
+ backend must authenticate each JWT against the current active credential
189
+ generation, expiry and exact service ownership. Structural snapshot helpers do
190
+ not perform these checks. Credential changes, encrypted service SecretSet
191
+ delivery and the replay receipt must commit in the same authorized transaction.
192
+ The response contains only the immutable secret/version reference; no bearer or
193
+ hash is returned. Revocation removes current delivery from the metadata.
194
+
177
195
  ### Node-Bound Runtime Assignments
178
196
 
179
197
  The `data` namespace exports `IRuntimeAssignmentV1`, canonical digest helpers,
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/interfaces',
6
- version: '30.1.0',
6
+ version: '30.2.0',
7
7
  description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
8
8
  }
package/ts/data/index.ts CHANGED
@@ -34,6 +34,7 @@ export * from './backup.js';
34
34
  export * from './clusternode.js';
35
35
  export * from './settings.js';
36
36
  export * from './service.js';
37
+ export * from './servicecredential.js';
37
38
  export * from './serviceports.js';
38
39
  export * from './status.js';
39
40
  export * from './taskexecution.js';
@@ -0,0 +1,130 @@
1
+ /** Workload permissions are independent of human membership and deployment grants. */
2
+ export type TServiceMachineCapabilityV1 = 'platform:session';
3
+
4
+ /** Exact existing-service authority. Never infer this grant from an API role. */
5
+ export interface IServiceMachineGrantV1 {
6
+ organizationId: string;
7
+ serviceId: string;
8
+ capabilities: TServiceMachineCapabilityV1[];
9
+ }
10
+
11
+ /** Public metadata only. The bearer is delivered through the owning service's SecretSet. */
12
+ export interface IServiceMachineCredentialMetadataV1 {
13
+ schemaVersion: 1;
14
+ credentialId: string;
15
+ userId: string;
16
+ grant: IServiceMachineGrantV1;
17
+ generation: number;
18
+ state: 'active' | 'revoked';
19
+ expiresAt: number;
20
+ createdAt: number;
21
+ updatedAt: number;
22
+ /** Exact immutable encrypted delivery version; null after revocation. */
23
+ delivery: { secretId: string; secretVersionId: string } | null;
24
+ }
25
+
26
+ interface IServiceMachineCredentialMutationBaseV1 {
27
+ schemaVersion: 1;
28
+ organizationId: string;
29
+ serviceId: string;
30
+ /** Retain the complete request and this ID across retries. */
31
+ mutationId: string;
32
+ }
33
+
34
+ /** Creation is absent-only; rotation and revocation require the exact current generation. */
35
+ export type TServiceMachineCredentialMutationV1 = IServiceMachineCredentialMutationBaseV1 & (
36
+ | {
37
+ operation: 'ensure';
38
+ expectedGeneration: null;
39
+ capabilities: TServiceMachineCapabilityV1[];
40
+ expiresAt: number;
41
+ }
42
+ | {
43
+ operation: 'rotate';
44
+ expectedGeneration: number;
45
+ capabilities: TServiceMachineCapabilityV1[];
46
+ expiresAt: number;
47
+ }
48
+ | { operation: 'revoke'; expectedGeneration: number }
49
+ );
50
+
51
+ export interface IServiceMachineCredentialMutationResultV1 {
52
+ /** Historical on replay; replay never restores revoked or superseded authority. */
53
+ credential: IServiceMachineCredentialMetadataV1;
54
+ replayed: boolean;
55
+ }
56
+
57
+ const fail = (): never => { throw new Error('Service credential contract rejected.'); };
58
+ function requireValue(value: unknown): asserts value { if (!value) fail(); }
59
+ const object = (value: unknown, keys: string[]): Record<string, unknown> => {
60
+ requireValue(value !== null && typeof value === 'object' && !Array.isArray(value));
61
+ requireValue(Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
62
+ const actual = Reflect.ownKeys(value);
63
+ requireValue(actual.length === keys.length && keys.every((key) => actual.includes(key)));
64
+ const copy: Record<string, unknown> = Object.create(null);
65
+ for (const key of keys) {
66
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
67
+ requireValue(descriptor && Object.hasOwn(descriptor, 'value') && descriptor.enumerable);
68
+ copy[key] = descriptor.value;
69
+ }
70
+ return copy;
71
+ };
72
+ const identifier = (value: unknown): string => {
73
+ requireValue(typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(value));
74
+ return value;
75
+ };
76
+ const counter = (value: unknown): number => {
77
+ requireValue(typeof value === 'number' && Number.isSafeInteger(value) && value > 0 && value < Number.MAX_SAFE_INTEGER);
78
+ return value;
79
+ };
80
+ const capabilities = (value: unknown): TServiceMachineCapabilityV1[] => {
81
+ requireValue(Array.isArray(value) && Object.getPrototypeOf(value) === Array.prototype);
82
+ const keys = Reflect.ownKeys(value);
83
+ requireValue(keys.length === 2 && keys.includes('length') && keys.includes('0'));
84
+ const entry = Object.getOwnPropertyDescriptor(value, '0');
85
+ requireValue(entry && Object.hasOwn(entry, 'value') && entry.enumerable && entry.value === 'platform:session');
86
+ return ['platform:session'];
87
+ };
88
+ const safe = <T>(operation: () => T): T => { try { return operation(); } catch { return fail(); } };
89
+
90
+ /** Structural copy only; the backend must fence live service ownership and credential authority. */
91
+ export const snapshotServiceMachineGrantV1 = (value: unknown): IServiceMachineGrantV1 => safe(() => {
92
+ const grant = object(value, ['organizationId', 'serviceId', 'capabilities']);
93
+ return { organizationId: identifier(grant.organizationId), serviceId: identifier(grant.serviceId),
94
+ capabilities: capabilities(grant.capabilities) };
95
+ });
96
+
97
+ export const snapshotServiceMachineCredentialMutationV1 = (value: unknown): TServiceMachineCredentialMutationV1 => safe(() => {
98
+ requireValue(value !== null && typeof value === 'object');
99
+ const operation = Object.getOwnPropertyDescriptor(value, 'operation')?.value;
100
+ requireValue(operation === 'ensure' || operation === 'rotate' || operation === 'revoke');
101
+ const request = object(value, ['schemaVersion', 'organizationId', 'serviceId', 'mutationId', 'operation',
102
+ 'expectedGeneration', ...(operation === 'revoke' ? [] : ['capabilities', 'expiresAt'])]);
103
+ requireValue(request.schemaVersion === 1);
104
+ const base = { schemaVersion: 1 as const, organizationId: identifier(request.organizationId),
105
+ serviceId: identifier(request.serviceId), mutationId: identifier(request.mutationId) };
106
+ if (operation === 'revoke') return { ...base, operation, expectedGeneration: counter(request.expectedGeneration) };
107
+ const material = { capabilities: capabilities(request.capabilities), expiresAt: counter(request.expiresAt) };
108
+ if (operation === 'ensure') {
109
+ requireValue(request.expectedGeneration === null);
110
+ return { ...base, ...material, operation, expectedGeneration: null };
111
+ }
112
+ return { ...base, ...material, operation, expectedGeneration: counter(request.expectedGeneration) };
113
+ });
114
+
115
+ export const snapshotServiceMachineCredentialMetadataV1 = (value: unknown): IServiceMachineCredentialMetadataV1 => safe(() => {
116
+ const metadata = object(value, ['schemaVersion', 'credentialId', 'userId', 'grant', 'generation', 'state',
117
+ 'expiresAt', 'createdAt', 'updatedAt', 'delivery']);
118
+ requireValue(metadata.schemaVersion === 1 && (metadata.state === 'active' || metadata.state === 'revoked'));
119
+ const createdAt = counter(metadata.createdAt);
120
+ const updatedAt = counter(metadata.updatedAt);
121
+ requireValue(updatedAt >= createdAt);
122
+ let delivery: IServiceMachineCredentialMetadataV1['delivery'] = null;
123
+ if (metadata.state === 'active') {
124
+ const version = object(metadata.delivery, ['secretId', 'secretVersionId']);
125
+ delivery = { secretId: identifier(version.secretId), secretVersionId: identifier(version.secretVersionId) };
126
+ } else requireValue(metadata.delivery === null);
127
+ return { schemaVersion: 1, credentialId: identifier(metadata.credentialId), userId: identifier(metadata.userId),
128
+ grant: snapshotServiceMachineGrantV1(metadata.grant), generation: counter(metadata.generation),
129
+ state: metadata.state, expiresAt: counter(metadata.expiresAt), createdAt, updatedAt, delivery };
130
+ });
@@ -1,5 +1,30 @@
1
1
  import * as plugins from '../plugins.js';
2
2
  import type { IIdentity, IIdentityCredential, IServiceDeploymentGrant } from '../data/user.js';
3
+ import type {
4
+ IServiceMachineCredentialMetadataV1, IServiceMachineCredentialMutationResultV1,
5
+ TServiceMachineCredentialMutationV1,
6
+ } from '../data/servicecredential.js';
7
+
8
+ export interface IReq_Admin_GetServiceMachineCredentialV1 extends plugins.typedrequestInterfaces.implementsTR<
9
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_GetServiceMachineCredentialV1
10
+ > {
11
+ method: 'getServiceMachineCredentialV1';
12
+ request: { identity: IIdentityCredential; organizationId: string; serviceId: string };
13
+ response: { credential: IServiceMachineCredentialMetadataV1 | null };
14
+ }
15
+
16
+ /**
17
+ * The backend authorizes the exact service and mutates hashed credential authority,
18
+ * encrypted SecretSet delivery and the replay receipt in one fenced transaction.
19
+ * No bearer is returned. Existing legacy deployment/cluster credentials are separate.
20
+ */
21
+ export interface IReq_Admin_MutateServiceMachineCredentialV1 extends plugins.typedrequestInterfaces.implementsTR<
22
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_MutateServiceMachineCredentialV1
23
+ > {
24
+ method: 'mutateServiceMachineCredentialV1';
25
+ request: { identity: IIdentityCredential; mutation: TServiceMachineCredentialMutationV1 };
26
+ response: IServiceMachineCredentialMutationResultV1;
27
+ }
3
28
 
4
29
  /** Backend-owned human credential metadata; never includes a password verifier. */
5
30
  export interface IHumanCredentialMetadata {