@astrale-os/kernel-protocol 0.5.0-beta.31 → 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,14 @@
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
+
27
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)
28
36
 
29
37
 
@@ -1,4 +1,4 @@
1
- export { MEDIA_TYPE, PATH, url, seal, accept, acceptStored, encode, type Publication, type StoredPublication, } from './publication.js';
1
+ export { MEDIA_TYPE, PATH, url, seal, accept, encode, type Publication } from './publication.js';
2
2
  export { acceptBundle, decodeBundle } from './bundle-admission.js';
3
3
  export { BUNDLE_MEDIA_TYPE, describeBundle, type BundleDescriptor } from './bundle.js';
4
4
  export { verifyBundleBytes } from './bundle-evidence.js';
@@ -1,4 +1,4 @@
1
- export { MEDIA_TYPE, PATH, url, seal, accept, acceptStored, encode, } from './publication.js';
1
+ export { MEDIA_TYPE, PATH, url, seal, accept, encode } from './publication.js';
2
2
  export { acceptBundle, decodeBundle } from './bundle-admission.js';
3
3
  export { BUNDLE_MEDIA_TYPE, describeBundle } from './bundle.js';
4
4
  export { verifyBundleBytes } from './bundle-evidence.js';
@@ -31,16 +31,6 @@ export interface Publication {
31
31
  readonly routes: readonly HttpRoute[];
32
32
  readonly delivery?: DeliveryEndpoint;
33
33
  }
34
- /** Canonical semantic Publication plus its exact coordinate in durable storage. */
35
- export interface StoredPublication {
36
- readonly publication: Publication;
37
- readonly deployedEtag: Etag;
38
- }
39
34
  export declare function seal(input: Content): Publication;
40
35
  export declare function accept(input: unknown): Publication;
41
- /**
42
- * Admit a canonical Publication retained before the admin-to-authority vocabulary cutover.
43
- * Network admission remains strict; this one-way reader exists only for already-trusted storage.
44
- */
45
- export declare function acceptStored(input: unknown): StoredPublication;
46
36
  export declare function encode(input: Publication): Uint8Array;
@@ -44,40 +44,6 @@ export function accept(input) {
44
44
  }
45
45
  return publication;
46
46
  }
47
- /**
48
- * Admit a canonical Publication retained before the admin-to-authority vocabulary cutover.
49
- * Network admission remains strict; this one-way reader exists only for already-trusted storage.
50
- */
51
- export function acceptStored(input) {
52
- try {
53
- const publication = accept(input);
54
- return deepFreeze({ publication, deployedEtag: publication.etag });
55
- }
56
- catch (currentFailure) {
57
- const value = record(input, 'PUBLICATION_INVALID', 'Expected a Publication object.');
58
- const requirements = record(value.requirements, 'PUBLICATION_INVALID', 'Publication requirements are invalid.');
59
- const capabilities = record(requirements.capabilities, 'PUBLICATION_INVALID', 'Publication capability requirements are invalid.');
60
- if (capabilities.admin === undefined)
61
- throw currentFailure;
62
- if (capabilities.authority !== undefined) {
63
- fail('PUBLICATION_INVALID', 'Publication cannot mix admin and authority requirements.');
64
- }
65
- const { etag, ...content } = value;
66
- const admittedEtag = admitDigest(etag);
67
- if (admittedEtag !== computeEtag(content)) {
68
- fail('PUBLICATION_ETAG_MISMATCH', 'Publication ETag does not match its canonical content.');
69
- }
70
- const { admin, ...currentCapabilities } = capabilities;
71
- const admitted = acceptPublication({
72
- ...value,
73
- requirements: {
74
- ...requirements,
75
- capabilities: { ...currentCapabilities, authority: admin },
76
- },
77
- }, true);
78
- return deepFreeze({ publication: seal(withoutEtag(admitted)), deployedEtag: admittedEtag });
79
- }
80
- }
81
47
  export function encode(input) {
82
48
  return portable.json.encode(input);
83
49
  }
@@ -1,10 +1,13 @@
1
- import { K } from '@astrale-os/kernel-core/schema';
2
1
  import { AuthorityOperationOrder } from '@astrale-os/kernel-core/schema/authority/operation';
3
- import { schema as schemaApi } from '@astrale-os/kernel-dsl/v1';
4
2
  import { acceptDefinitionRef, callableAddress, Key } from '@astrale-os/kernel-dsl/v1/addressing';
5
3
  import { revision } from '@astrale-os/kernel-dsl/v1/schema/revision';
6
4
  import { array, deepFreeze, exact, record, unique } from './content.js';
7
5
  import { fail } from './error.js';
6
+ const KernelIdentityClass = Object.freeze({
7
+ origin: 'kernel.astrale.ai',
8
+ kind: 'class',
9
+ name: 'Identity',
10
+ });
8
11
  export const ClassOperationOrder = Object.freeze([
9
12
  'create',
10
13
  'read',
@@ -106,18 +109,11 @@ export function validateRequirements(root, closure, requirements) {
106
109
  fail('PUBLICATION_INVALID', 'Class capability requirement names an unknown dependency Class.');
107
110
  }
108
111
  }
109
- const resolvedRoot = requirements.capabilities.profiles === undefined
110
- ? undefined
111
- : schemaApi.resolve(schemaApi.accept(root, {
112
- get(origin, expectedRevision) {
113
- return closure.find((dependency) => dependency.origin === origin && revision(dependency) === expectedRevision);
114
- },
115
- }));
116
112
  for (const profile of requirements.capabilities.profiles ?? []) {
117
- const subject = resolvedRoot.core.nodes[profile.subject.name];
113
+ const subject = root.core.nodes[profile.subject.name];
118
114
  if (profile.subject.origin !== root.origin ||
119
115
  subject === undefined ||
120
- !subject.class.satisfies(K.classes.Identity)) {
116
+ !classSatisfies(root, dependencies, subject.class, KernelIdentityClass)) {
121
117
  fail('PUBLICATION_INVALID', 'Capability profile subject must name one local Core Identity.');
122
118
  }
123
119
  for (const callable of profile.functions ?? []) {
@@ -137,6 +133,22 @@ export function validateRequirements(root, closure, requirements) {
137
133
  }
138
134
  }
139
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);
151
+ }
140
152
  function exactDependencyClosure(root, closure) {
141
153
  const supplied = new Map(closure.map((dependency) => [dependency.origin, dependency]));
142
154
  const reachable = new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/kernel-protocol",
3
- "version": "0.5.0-beta.31",
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": {