@astrale-os/kernel-protocol 0.5.0-beta.30 → 0.5.0-beta.32

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
@@ -24,6 +24,34 @@
24
24
  * the first supported V2 release is a coordinated `1.0.0`, not a compatible continuation of the
25
25
  current `0.x` line; package and wire versions remain independent as specified in
26
26
  [the protocol architecture](./.spec/architecture.md)
27
+ ## [0.5.0-beta.32](https://github.com/astrale-os/kernel/compare/kernel-protocol-v0.5.0-beta.31...kernel-protocol-v0.5.0-beta.32) (2026-08-31)
28
+
29
+
30
+ ### Bug Fixes
31
+
32
+ * **api:** keep profile validation runtime-safe ([#589](https://github.com/astrale-os/kernel/issues/589)) ([fd5c5f6](https://github.com/astrale-os/kernel/commit/fd5c5f6e7e6b2a4b617ccffe9efa605c05ea0c52))
33
+ * **protocol:** keep profile identity reference portable ([#592](https://github.com/astrale-os/kernel/issues/592)) ([c942216](https://github.com/astrale-os/kernel/commit/c942216fa52c0344ef74cf1dea49229e7981d9bf))
34
+
35
+ ## [0.5.0-beta.31](https://github.com/astrale-os/kernel/compare/kernel-protocol-v0.5.0-beta.30...kernel-protocol-v0.5.0-beta.31) (2026-08-31)
36
+
37
+
38
+ ### Features
39
+
40
+ * **auth:** enforce two-gate Function authority ([#561](https://github.com/astrale-os/kernel/issues/561)) ([c681d4b](https://github.com/astrale-os/kernel/commit/c681d4b391520906b2cad9171cd251962b200faf))
41
+
42
+
43
+ ### Bug Fixes
44
+
45
+ * **runtime:** recover legacy Publication authority safely ([#577](https://github.com/astrale-os/kernel/issues/577)) ([76e432a](https://github.com/astrale-os/kernel/commit/76e432a710f579c103a245260fb4952e7dc58ac8))
46
+
47
+
48
+ ### Dependencies
49
+
50
+ * The following workspace dependencies were updated
51
+ * dependencies
52
+ * @astrale-os/kernel-core bumped to 0.9.0-beta.29
53
+ * @astrale-os/kernel-dsl bumped to 0.2.0-beta.22
54
+
27
55
  ## [0.5.0-beta.30](https://github.com/astrale-os/kernel/compare/kernel-protocol-v0.5.0-beta.29...kernel-protocol-v0.5.0-beta.30) (2026-08-30)
28
56
 
29
57
 
@@ -10,7 +10,7 @@ export { PublicationError, type PublicationErrorCode } from './error.js';
10
10
  export * as delivery from './delivery/index.js';
11
11
  export type { Authentication as DeliveryAuthentication, Endpoint as DeliveryEndpoint, } from './delivery-endpoint.js';
12
12
  export type { Manifest } from './manifest.js';
13
- export { ClassOperationOrder, admitRequirements, validateRequirements, type CapabilityAuthorityRequirements, type CapabilityRequirements, type ClassCapabilityRequirement, type ClassOperation, type NonEmpty, type PublicationRequirements, } from './requirements.js';
13
+ export { ClassOperationOrder, admitRequirements, validateRequirements, type CapabilityAuthorityRequirements, type CapabilityProfileRequirement, type CapabilityRequirements, type ClassCapabilityRequirement, type ClassOperation, type NonEmpty, type PublicationRequirements, } from './requirements.js';
14
14
  export { acceptReference, compareReference, compareReferences, matchesReference, reference, type PublicationRef, type PublicationReferenceRelation, } from './reference.js';
15
15
  export type { BodyPick, CredentialSource, HttpMethod, HttpRoute, HttpRouteDeclaration, RouteBody, RouteHeaders, SingleCredentialSource, } from './route.js';
16
16
  export type { Schema } from './schema.js';
@@ -1,6 +1,6 @@
1
1
  import type { AuthorityOperation } from '@astrale-os/kernel-core/schema/authority/operation';
2
- import type { schema } from '@astrale-os/kernel-dsl/v1';
3
- import type { ClassRef, Key as SemanticKey } from '@astrale-os/kernel-dsl/v1/addressing';
2
+ import type { schema as schemaLanguage } from '@astrale-os/kernel-dsl/v1';
3
+ import type { ClassRef, CoreRef, Key as SemanticKey } from '@astrale-os/kernel-dsl/v1/addressing';
4
4
  export type NonEmpty<Value> = readonly [Value, ...Value[]];
5
5
  export declare const ClassOperationOrder: readonly ["create", "read", "traverse", "update", "delete"];
6
6
  export type ClassOperation = (typeof ClassOperationOrder)[number];
@@ -16,7 +16,14 @@ export interface CapabilityRequirements {
16
16
  readonly functions?: NonEmpty<SemanticKey.Callable>;
17
17
  readonly classes?: NonEmpty<ClassCapabilityRequirement>;
18
18
  readonly authority?: CapabilityAuthorityRequirements;
19
- readonly admin?: never;
19
+ readonly profiles?: NonEmpty<CapabilityProfileRequirement>;
20
+ }
21
+ /** Installation-owned authority materialized onto one local Core Identity. */
22
+ export interface CapabilityProfileRequirement {
23
+ readonly subject: CoreRef;
24
+ readonly functions?: NonEmpty<SemanticKey.Callable>;
25
+ readonly classes?: NonEmpty<ClassCapabilityRequirement>;
26
+ readonly authority?: CapabilityAuthorityRequirements;
20
27
  }
21
28
  export interface PublicationRequirements {
22
29
  readonly capabilities: CapabilityRequirements;
@@ -24,4 +31,4 @@ export interface PublicationRequirements {
24
31
  /** Admit the exact closed Publication v2 requirements object. */
25
32
  export declare function admitRequirements(input: unknown): PublicationRequirements;
26
33
  /** Bind explicit capability targets to exact functional dependencies in the bundle closure. */
27
- export declare function validateRequirements(root: schema.DomainSchema, closure: readonly schema.DomainSchema[], requirements: PublicationRequirements): void;
34
+ export declare function validateRequirements(root: schemaLanguage.DomainSchema, closure: readonly schemaLanguage.DomainSchema[], requirements: PublicationRequirements): void;
@@ -3,6 +3,11 @@ import { acceptDefinitionRef, callableAddress, Key } from '@astrale-os/kernel-ds
3
3
  import { revision } from '@astrale-os/kernel-dsl/v1/schema/revision';
4
4
  import { array, deepFreeze, exact, record, unique } from './content.js';
5
5
  import { fail } from './error.js';
6
+ const KernelIdentityClass = Object.freeze({
7
+ origin: 'kernel.astrale.ai',
8
+ kind: 'class',
9
+ name: 'Identity',
10
+ });
6
11
  export const ClassOperationOrder = Object.freeze([
7
12
  'create',
8
13
  'read',
@@ -15,7 +20,7 @@ export function admitRequirements(input) {
15
20
  const requirements = record(input, 'PUBLICATION_INVALID', 'Publication requirements are invalid.');
16
21
  exact(requirements, ['capabilities'], 'PUBLICATION_INVALID');
17
22
  const capabilities = record(requirements.capabilities, 'PUBLICATION_INVALID', 'Publication capability requirements are invalid.');
18
- exact(capabilities, ['authority', 'classes', 'functions'], 'PUBLICATION_INVALID');
23
+ exact(capabilities, ['authority', 'classes', 'functions', 'profiles'], 'PUBLICATION_INVALID');
19
24
  const functions = capabilities.functions === undefined
20
25
  ? undefined
21
26
  : nonEmpty(capabilities.functions, 'Function capability requirements').map(admitCallable);
@@ -33,6 +38,13 @@ export function admitRequirements(input) {
33
38
  const authority = capabilities.authority === undefined
34
39
  ? undefined
35
40
  : admitAuthorityRequirements(capabilities.authority);
41
+ const profiles = capabilities.profiles === undefined
42
+ ? undefined
43
+ : nonEmpty(capabilities.profiles, 'Capability profiles').map(admitCapabilityProfile);
44
+ if (profiles !== undefined) {
45
+ unique(profiles.map(({ subject }) => Key.of(subject)), 'PUBLICATION_INVALID', 'Capability profile subject');
46
+ canonical(profiles, (left, right) => compare(Key.of(left.subject), Key.of(right.subject)), 'Capability profiles');
47
+ }
36
48
  return deepFreeze({
37
49
  capabilities: {
38
50
  ...(functions === undefined
@@ -42,9 +54,45 @@ export function admitRequirements(input) {
42
54
  ? {}
43
55
  : { classes: Object.freeze(classes) }),
44
56
  ...(authority === undefined ? {} : { authority }),
57
+ ...(profiles === undefined
58
+ ? {}
59
+ : { profiles: Object.freeze(profiles) }),
45
60
  },
46
61
  });
47
62
  }
63
+ function admitCapabilityProfile(input) {
64
+ const profile = record(input, 'PUBLICATION_INVALID', 'Capability profile requirement is invalid.');
65
+ exact(profile, ['authority', 'classes', 'functions', 'subject'], 'PUBLICATION_INVALID');
66
+ const subject = admitCoreRef(profile.subject);
67
+ const functions = profile.functions === undefined
68
+ ? undefined
69
+ : nonEmpty(profile.functions, 'Capability profile Function requirements').map(admitCallable);
70
+ if (functions !== undefined) {
71
+ unique(functions, 'PUBLICATION_INVALID', 'Capability profile Function requirement');
72
+ canonical(functions, compare, 'Capability profile Function requirements');
73
+ }
74
+ const classes = profile.classes === undefined
75
+ ? undefined
76
+ : nonEmpty(profile.classes, 'Capability profile Class requirements').map(admitClassRequirement);
77
+ if (classes !== undefined) {
78
+ unique(classes.map((entry) => classKey(entry.class)), 'PUBLICATION_INVALID', 'Capability profile Class requirement');
79
+ canonical(classes, (left, right) => compare(classKey(left.class), classKey(right.class)), 'Capability profile Class requirements');
80
+ }
81
+ const authority = profile.authority === undefined ? undefined : admitAuthorityRequirements(profile.authority);
82
+ if (functions === undefined && classes === undefined && authority === undefined) {
83
+ fail('PUBLICATION_INVALID', 'Capability profile requirements cannot be empty.');
84
+ }
85
+ return Object.freeze({
86
+ subject,
87
+ ...(functions === undefined
88
+ ? {}
89
+ : { functions: Object.freeze(functions) }),
90
+ ...(classes === undefined
91
+ ? {}
92
+ : { classes: Object.freeze(classes) }),
93
+ ...(authority === undefined ? {} : { authority }),
94
+ });
95
+ }
48
96
  /** Bind explicit capability targets to exact functional dependencies in the bundle closure. */
49
97
  export function validateRequirements(root, closure, requirements) {
50
98
  const dependencies = exactDependencyClosure(root, closure);
@@ -61,6 +109,45 @@ export function validateRequirements(root, closure, requirements) {
61
109
  fail('PUBLICATION_INVALID', 'Class capability requirement names an unknown dependency Class.');
62
110
  }
63
111
  }
112
+ for (const profile of requirements.capabilities.profiles ?? []) {
113
+ const subject = root.core.nodes[profile.subject.name];
114
+ if (profile.subject.origin !== root.origin ||
115
+ subject === undefined ||
116
+ !classSatisfies(root, dependencies, subject.class, KernelIdentityClass)) {
117
+ fail('PUBLICATION_INVALID', 'Capability profile subject must name one local Core Identity.');
118
+ }
119
+ for (const callable of profile.functions ?? []) {
120
+ const dependency = capabilityOwner(root, dependencies, callable, 'Function');
121
+ if (!hasCallable(dependency, callable)) {
122
+ fail('PUBLICATION_INVALID', 'Capability profile Function requirement names an unknown dependency callable.');
123
+ }
124
+ }
125
+ for (const requirement of profile.classes ?? []) {
126
+ const key = Key.of(requirement.class);
127
+ const owner = requirement.class.origin === root.origin
128
+ ? root
129
+ : capabilityOwner(root, dependencies, key, 'Class');
130
+ if (owner.classes[requirement.class.name] === undefined) {
131
+ fail('PUBLICATION_INVALID', 'Capability profile Class requirement names an unknown dependency Class.');
132
+ }
133
+ }
134
+ }
135
+ }
136
+ function classSatisfies(root, dependencies, candidate, expected) {
137
+ const expectedKey = Key.of(expected);
138
+ const visited = new Set();
139
+ const visit = (reference) => {
140
+ const key = Key.of(reference);
141
+ if (key === expectedKey)
142
+ return true;
143
+ if (visited.has(key))
144
+ return false;
145
+ visited.add(key);
146
+ const owner = reference.origin === root.origin ? root : dependencies.get(reference.origin);
147
+ const definition = owner?.classes[reference.name];
148
+ return definition?.kind === 'node' && definition.extends.some(visit);
149
+ };
150
+ return visit(candidate);
64
151
  }
65
152
  function exactDependencyClosure(root, closure) {
66
153
  const supplied = new Map(closure.map((dependency) => [dependency.origin, dependency]));
@@ -136,6 +223,19 @@ function admitClassRef(input) {
136
223
  fail('PUBLICATION_INVALID', 'Class capability reference is invalid.');
137
224
  return value;
138
225
  }
226
+ function admitCoreRef(input) {
227
+ let value;
228
+ try {
229
+ value = acceptDefinitionRef(input);
230
+ }
231
+ catch {
232
+ fail('PUBLICATION_INVALID', 'Capability profile subject is invalid.');
233
+ }
234
+ if (value.kind !== 'core') {
235
+ fail('PUBLICATION_INVALID', 'Capability profile subject must be one local Core reference.');
236
+ }
237
+ return value;
238
+ }
139
239
  function nonEmpty(input, label) {
140
240
  const values = [...array(input, 'PUBLICATION_INVALID')];
141
241
  if (values.length === 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/kernel-protocol",
3
- "version": "0.5.0-beta.30",
3
+ "version": "0.5.0-beta.32",
4
4
  "description": "Portable Astrale invocation, publication, installation, routing, and issuer protocols",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -133,8 +133,8 @@
133
133
  "access": "public"
134
134
  },
135
135
  "dependencies": {
136
- "@astrale-os/kernel-core": "0.9.0-beta.28",
137
- "@astrale-os/kernel-dsl": "0.2.0-beta.21",
136
+ "@astrale-os/kernel-core": "0.9.0-beta.29",
137
+ "@astrale-os/kernel-dsl": "0.2.0-beta.22",
138
138
  "@noble/hashes": "2.3.0"
139
139
  },
140
140
  "devDependencies": {