@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
|
|
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:
|
|
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.
|
|
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.
|
|
137
|
-
"@astrale-os/kernel-dsl": "0.2.0-beta.
|
|
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": {
|