@astrale-os/sdk 0.6.0-beta.24 → 0.6.0-beta.26

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.
Files changed (37) hide show
  1. package/README.md +6 -6
  2. package/dist/application/action/context.d.ts +29 -25
  3. package/dist/application/action/index.d.ts +1 -1
  4. package/dist/application/dependency/invoker.d.ts +5 -5
  5. package/dist/application/integration/operation.d.ts +2 -2
  6. package/dist/application/runtime/workflows.d.ts +1 -1
  7. package/dist/application/runtime/workflows.js +1 -4
  8. package/dist/application/workflow/context.d.ts +1 -1
  9. package/dist/application/workflow/define.d.ts +1 -1
  10. package/dist/application/workflow/define.js +1 -1
  11. package/dist/application/workflow/workflow.d.ts +1 -3
  12. package/dist/execution/identity/authentication.d.ts +10 -1
  13. package/dist/execution/identity/authentication.js +21 -0
  14. package/dist/execution/identity/identity.js +5 -1
  15. package/dist/execution/invocation/admission/authentication.d.ts +8 -5
  16. package/dist/execution/invocation/admission/authentication.js +39 -21
  17. package/dist/execution/invocation/admission/authority.d.ts +17 -1
  18. package/dist/execution/invocation/admission/authority.js +5 -2
  19. package/dist/execution/invocation/admission/index.d.ts +2 -2
  20. package/dist/execution/invocation/admission/index.js +1 -1
  21. package/dist/execution/invocation/dependency/bind.d.ts +2 -0
  22. package/dist/execution/invocation/dependency/bind.js +1 -2
  23. package/dist/execution/invocation/dispatch/context.d.ts +5 -5
  24. package/dist/execution/invocation/dispatch/context.js +16 -30
  25. package/dist/execution/invocation/dispatch/workflow.js +0 -4
  26. package/dist/execution/invocation/integration/call.d.ts +2 -1
  27. package/dist/execution/invocation/integration/call.js +1 -1
  28. package/dist/execution/invocation/integration/clients.d.ts +1 -1
  29. package/dist/tooling/cli/publish/index.d.ts +12 -4
  30. package/dist/tooling/cli/publish/index.js +43 -5
  31. package/dist/tooling/cli/registry/client.d.ts +7 -0
  32. package/dist/tooling/cli/registry/client.js +8 -0
  33. package/dist/tooling/cli/registry/executable.d.ts +6 -0
  34. package/dist/tooling/cli/registry/executable.js +6 -0
  35. package/dist/tooling/cli/registry/index.d.ts +1 -1
  36. package/dist/tooling/cli/registry/index.js +1 -1
  37. package/package.json +9 -8
package/README.md CHANGED
@@ -5,10 +5,8 @@ single-step Actions, explicit multi-step Workflows, provider-neutral Integration
5
5
  authoring, Domain composition, Deployment, and Execution without exposing DSL callable
6
6
  topology in implementation files.
7
7
 
8
- This draft moves the Domain name and entrypoint into its root Schema. The source is rebased on
9
- current SDK main, but the required canonical Kernel Schema contract is not yet published in the
10
- pinned dependency cohort. See [the migration and qualification boundary](docs/domain-schema-migration.md)
11
- before using or releasing this branch.
8
+ The Domain name and optional View entrypoint belong to its root Schema. See
9
+ [the Schema authoring contract](docs/domain-schema-migration.md).
12
10
 
13
11
  [![CI](https://github.com/astrale-os/sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/astrale-os/sdk/actions/workflows/ci.yml)
14
12
  [![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
@@ -60,11 +58,13 @@ Install the URL printed by `astrale-domain deploy` on each intended instance.
60
58
 
61
59
  ## Handler authority and dependencies
62
60
 
63
- Authenticated handlers use Domain self authority for `kernel`, `graph`, `query`, and `mutate`.
61
+ Handlers use Domain self authority for `kernel`, `graph`, `query`, and `mutate`.
64
62
  `dependencies.directory.caller.invoke(d => d.functions.lookup, input)` calls a direct declared
65
63
  dependency on behalf of the incoming caller Identity; `.self` calls it as the executing Domain.
66
64
  Explicit `caller` or `self` authority is also available on `kernel` and `graph`; the former
67
- handler `client` is removed. See [the API and migration guide](docs/handler-dependencies.md).
65
+ handler `client` is removed. A call to an `auth: 'anonymous'` callable that carries no credential
66
+ has no caller: its handler acts as the Domain alone, without the `caller` selections. See
67
+ [the API and migration guide](docs/handler-dependencies.md).
68
68
 
69
69
  ## Durable Workflows
70
70
 
@@ -47,37 +47,41 @@ export interface DomainGraphExecutor<DomainValue extends Domain = Domain> {
47
47
  readonly query: QueryExecutor<DomainValue>;
48
48
  readonly mutate: MutationExecutor<DomainValue>;
49
49
  }
50
- export type DomainGraphExecutors<DomainValue extends Domain = Domain> = Readonly<DomainGraphExecutor<DomainValue> & Record<'caller' | 'self', DomainGraphExecutor<DomainValue>>>;
50
+ /** Whose authority one Kernel call exercises: the incoming caller's or the installed Domain's. */
51
+ export type AuthorityMode = 'caller' | 'self';
52
+ /**
53
+ * One value per selectable authority. `self` exists in every invocation; `caller` exists only when
54
+ * the call carried a credential, so it is absent from a call that carried none.
55
+ */
56
+ export type AuthoritySelection<Value, Selectable extends AuthorityMode = AuthorityMode> = Readonly<Record<Selectable, Value> & {
57
+ readonly [Absent in Exclude<AuthorityMode, Selectable>]?: never;
58
+ }>;
59
+ export type DomainGraphExecutors<DomainValue extends Domain = Domain, Selectable extends AuthorityMode = AuthorityMode> = Readonly<DomainGraphExecutor<DomainValue> & AuthoritySelection<DomainGraphExecutor<DomainValue>, Selectable>>;
51
60
  /** Self-default Kernel session with one level of explicit authority selection. */
52
- export type DomainKernelSessions = Readonly<BoundClientSession & Record<'caller' | 'self', BoundClientSession>>;
53
- type AuthorityOf<Callable, DomainValue extends Domain> = Callable extends {
54
- readonly auth: 'anonymous';
55
- } ? {
56
- readonly caller: AnonymousCaller;
57
- readonly dependencies: null;
58
- readonly kernel: null;
59
- readonly graph: null;
60
- readonly query?: never;
61
- readonly mutate?: never;
62
- } | {
63
- readonly caller: AuthenticatedCaller;
64
- readonly dependencies: HandlerDependencies<DomainValue>;
65
- readonly kernel: DomainKernelSessions;
66
- readonly graph: DomainGraphExecutors<DomainValue>;
61
+ export type DomainKernelSessions<Selectable extends AuthorityMode = AuthorityMode> = Readonly<BoundClientSession & AuthoritySelection<BoundClientSession, Selectable>>;
62
+ /** What a handler holds toward the Kernel: the installed Domain authority by default. */
63
+ interface DomainCapabilities<DomainValue extends Domain, Selectable extends AuthorityMode> {
64
+ readonly dependencies: HandlerDependencies<DomainValue, Selectable>;
65
+ readonly kernel: DomainKernelSessions<Selectable>;
66
+ readonly graph: DomainGraphExecutors<DomainValue, Selectable>;
67
67
  /** Convenience executor using the installed Domain authority (`graph.self`). */
68
68
  readonly query: QueryExecutor<DomainValue>;
69
69
  /** Convenience executor using the installed Domain authority (`graph.self`). */
70
70
  readonly mutate: MutationExecutor<DomainValue>;
71
- } : {
71
+ }
72
+ /**
73
+ * A call to an `anonymous` callable that carries no credential has no caller, and its handler acts
74
+ * as the installed Domain alone. Every other call also selects its authenticated caller.
75
+ */
76
+ type AuthorityOf<Callable, DomainValue extends Domain> = Callable extends {
77
+ readonly auth: 'anonymous';
78
+ } ? ({
79
+ readonly caller: AnonymousCaller;
80
+ } & DomainCapabilities<DomainValue, 'self'>) | ({
72
81
  readonly caller: AuthenticatedCaller;
73
- readonly dependencies: HandlerDependencies<DomainValue>;
74
- readonly kernel: DomainKernelSessions;
75
- readonly graph: DomainGraphExecutors<DomainValue>;
76
- /** Convenience executor using the installed Domain authority (`graph.self`). */
77
- readonly query: QueryExecutor<DomainValue>;
78
- /** Convenience executor using the installed Domain authority (`graph.self`). */
79
- readonly mutate: MutationExecutor<DomainValue>;
80
- };
82
+ } & DomainCapabilities<DomainValue, AuthorityMode>) : {
83
+ readonly caller: AuthenticatedCaller;
84
+ } & DomainCapabilities<DomainValue, AuthorityMode>;
81
85
  type SelfOf<Callable> = Callable extends ResolvedMethod<unknown, unknown, false> ? {
82
86
  readonly self: NodeId;
83
87
  } : {
@@ -1,5 +1,5 @@
1
1
  export { defineAction, isAction } from './define.js';
2
2
  export type { Action, ActionAddress, ActionAddressOf, ActionDefinition, CallableAtAddress, LocalCallable, ResolvedDomainOf, } from './action.js';
3
- export type { ActionContext, ActionBinaryInput, ActionExecution, AnonymousCaller, AuthenticatedCaller, Caller, DomainGraphExecutor, DomainGraphExecutors, DomainKernelSessions, ResolvedCallable, } from './context.js';
3
+ export type { ActionContext, ActionBinaryInput, ActionExecution, AnonymousCaller, AuthenticatedCaller, AuthorityMode, AuthoritySelection, Caller, DomainGraphExecutor, DomainGraphExecutors, DomainKernelSessions, ResolvedCallable, } from './context.js';
4
4
  export type { ActionBinary, ActionOutputOf } from './output.js';
5
5
  export type { DeclaredErrorDefinition, DeclaredErrorFactory, DeclaredErrorFamily, DeclaredErrorOptions, DeclaredErrors, } from '../error/index.js';
@@ -2,7 +2,7 @@ import type { Binary, Input } from '../../platform/client/index.js';
2
2
  import type { SessionRequestOptions, SessionRequestOptionsWithBody } from '../../platform/client/session/index.js';
3
3
  import type { NodeId } from '../../platform/graph/node/index.js';
4
4
  import type { CallableInputOf, CallableInputModeOf, CallableOutputOf, CallableResultOf, Domain, ResolvedFunction, ResolvedMethod, KernelSchema, schema } from '../../platform/schema/index.js';
5
- import type { LocalCallable } from '../action/index.js';
5
+ import type { AuthorityMode, AuthoritySelection, LocalCallable } from '../action/index.js';
6
6
  export type DependencyCallOptions = Pick<SessionRequestOptions, 'timeoutMs' | 'idempotencyKey' | 'body'>;
7
7
  export type DependencyBinaryCallOptions = Pick<SessionRequestOptionsWithBody, 'timeoutMs' | 'idempotencyKey' | 'body'>;
8
8
  type InstanceMethodOf<D extends Domain> = Extract<LocalCallable<D>, ResolvedMethod<unknown, unknown, false>>;
@@ -22,10 +22,10 @@ export interface DependencyInvoker<D extends Domain> {
22
22
  }
23
23
  /**
24
24
  * Direct Schema aliases with explicit authority selection and no default invoke.
25
- * self is the executing Domain; caller forwards the incoming caller Identity.
26
- * The canonical Kernel is exposed through context.kernel.
25
+ * self is the executing Domain; caller forwards the incoming caller Identity, and is absent from a
26
+ * call that carried no credential. The canonical Kernel is exposed through context.kernel.
27
27
  */
28
- export type HandlerDependencies<D extends Domain> = Readonly<{
29
- [Alias in keyof D['dependencies'] as D['dependencies'][Alias] extends schema.DomainOf<KernelSchema> ? never : Alias]: Readonly<Record<'caller' | 'self', DependencyInvoker<Extract<D['dependencies'][Alias], Domain>>>>;
28
+ export type HandlerDependencies<D extends Domain, Selectable extends AuthorityMode = AuthorityMode> = Readonly<{
29
+ [Alias in keyof D['dependencies'] as D['dependencies'][Alias] extends schema.DomainOf<KernelSchema> ? never : Alias]: AuthoritySelection<DependencyInvoker<Extract<D['dependencies'][Alias], Domain>>, Selectable>;
30
30
  }>;
31
31
  export {};
@@ -19,9 +19,9 @@ export interface IntegrationExecution {
19
19
  readonly signal: AbortSignal;
20
20
  readonly deadline: number;
21
21
  readonly idempotencyKey?: string;
22
- /** Exact caller-bound invocation capability; absent for an anonymous invocation. */
22
+ /** Exact caller-bound invocation capability; absent when the call carried no credential. */
23
23
  readonly invoke?: BoundClientSession['invoke'];
24
- /** Domain-self transport authentication; absent for an anonymous invocation. */
24
+ /** Domain-self transport authentication; absent only outside a Kernel invocation. */
25
25
  readonly auth?: IntegrationAuthentication;
26
26
  }
27
27
  export interface IntegrationOperation<Input = unknown, Output = unknown> {
@@ -9,5 +9,5 @@ export interface RuntimeWorkflow {
9
9
  /** Declared `execution: { durable: true }`: runs on the workflow engine, never inline. */
10
10
  readonly durable: boolean;
11
11
  }
12
- /** Associate admitted Workflows with their exact authenticated value callables. */
12
+ /** Associate admitted Workflows with their exact value callables. */
13
13
  export declare function admitWorkflows(callables: readonly RuntimeCallable[], input: unknown): readonly RuntimeWorkflow[];
@@ -1,6 +1,6 @@
1
1
  import { isWorkflow } from '../workflow/index.js';
2
2
  import { runtimeCallableAt } from './callables.js';
3
- /** Associate admitted Workflows with their exact authenticated value callables. */
3
+ /** Associate admitted Workflows with their exact value callables. */
4
4
  export function admitWorkflows(callables, input) {
5
5
  if (!Array.isArray(input))
6
6
  throw new TypeError('Runtime workflows must be an array.');
@@ -16,9 +16,6 @@ export function admitWorkflows(callables, input) {
16
16
  if (callable === undefined) {
17
17
  throw new TypeError(`Runtime Workflow ${definition.address} is not root-local and executable.`);
18
18
  }
19
- if (callable.auth === 'anonymous') {
20
- throw new TypeError(`Runtime Workflow ${definition.address} must be authenticated.`);
21
- }
22
19
  if (callable.output.mode !== 'value') {
23
20
  throw new TypeError(`Runtime Workflow ${definition.address} must have value output.`);
24
21
  }
@@ -4,7 +4,7 @@ import type { DeclaredErrors, NoDeclaredErrors } from '../error/index.js';
4
4
  import type { Integrations } from '../integration/index.js';
5
5
  import type { DurableCallable, DurableWorkflowContext } from './durable.js';
6
6
  import type { WorkflowStep } from './step/index.js';
7
- /** Multi-step context of one authenticated value-output callable run inside its invocation. */
7
+ /** Multi-step context of one value-output callable run inside its invocation. */
8
8
  export type InlineWorkflowContext<Callable extends ResolvedCallable, DomainValue extends Domain = Domain, Definitions extends Integrations = Readonly<Record<never, never>>, Errors extends DeclaredErrors = NoDeclaredErrors> = ActionContext<Callable, DomainValue, Definitions, Errors> & {
9
9
  readonly step: WorkflowStep;
10
10
  };
@@ -3,7 +3,7 @@ import type { ResolvedDomainOf } from '../action/index.js';
3
3
  import type { DeclaredErrors, NoDeclaredErrors } from '../error/index.js';
4
4
  import type { Integrations } from '../integration/index.js';
5
5
  import type { CallableAtWorkflowAddress, Workflow, WorkflowAddress, WorkflowDefinition, WorkflowOptions } from './workflow.js';
6
- /** Define one authenticated multi-step value-output implementation by its SDK Action address. */
6
+ /** Define one multi-step value-output implementation by its SDK Action address. */
7
7
  export declare function defineWorkflow<Schema extends schema.DomainSchema, Definitions extends Integrations = Readonly<Record<never, never>>>(): {
8
8
  <const Address extends WorkflowAddress<Schema>>(address: Address, run: Workflow<CallableAtWorkflowAddress<Schema, Address>, Definitions, ResolvedDomainOf<Schema>, NoDeclaredErrors>): WorkflowDefinition<Schema, Address, Definitions, NoDeclaredErrors>;
9
9
  <const Address extends WorkflowAddress<Schema>, const Errors extends DeclaredErrors = NoDeclaredErrors>(address: Address, options: WorkflowOptions<Errors>, run: Workflow<CallableAtWorkflowAddress<Schema, Address>, Definitions, ResolvedDomainOf<Schema>, Errors>): WorkflowDefinition<Schema, Address, Definitions, Errors>;
@@ -2,7 +2,7 @@ import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
2
2
  import { rememberDeclaredErrors } from '../error/index.js';
3
3
  const admittedWorkflows = new WeakSet();
4
4
  const COMPAT_TOKEN = /^[A-Za-z0-9._~-]{1,64}$/u;
5
- /** Define one authenticated multi-step value-output implementation by its SDK Action address. */
5
+ /** Define one multi-step value-output implementation by its SDK Action address. */
6
6
  export function defineWorkflow() {
7
7
  return function define(address, optionsOrRun, possibleRun) {
8
8
  acceptWorkflowAddress(address);
@@ -3,9 +3,7 @@ import type { ActionAddressOf, ActionOutputOf, CallableAtAddress, LocalCallable,
3
3
  import type { DeclaredErrors, NoDeclaredErrors } from '../error/index.js';
4
4
  import type { Integrations } from '../integration/index.js';
5
5
  import type { WorkflowContext } from './context.js';
6
- export type WorkflowCallable<Callable> = Callable extends ResolvedCallable ? Callable extends {
7
- readonly auth: 'anonymous';
8
- } ? never : CallableOutputOf<Callable> extends {
6
+ export type WorkflowCallable<Callable> = Callable extends ResolvedCallable ? CallableOutputOf<Callable> extends {
9
7
  readonly mode: 'value';
10
8
  } ? Callable : never : never;
11
9
  export type WorkflowAddress<Schema extends schema.DomainSchema> = ActionAddressOf<WorkflowCallable<LocalCallable<ResolvedDomainOf<Schema>>>>;
@@ -1,6 +1,6 @@
1
1
  import type { Fetch } from '@astrale-os/kernel-client';
2
2
  import type { CredentialInput, IdentityId, IssuerId } from '../../platform/auth/index.js';
3
- import type { AuthenticatedInvocation } from '../invocation/index.js';
3
+ import type { AuthenticatedInvocation, SelfInvocation } from '../invocation/index.js';
4
4
  import type { InvocationAdmissionInput } from '../invocation/index.js';
5
5
  import type { IdentityNetworkPolicy, VerificationBasis, VerificationBasisResolver } from './jwks.js';
6
6
  import type { SigningMaterial } from './signing.js';
@@ -26,6 +26,15 @@ export declare function authenticate(credentialInput: CredentialInput, expectedK
26
26
  readonly deadline: number;
27
27
  readonly signal: AbortSignal;
28
28
  }, configuration: AuthenticationConfiguration): Promise<AuthenticatedInvocation>;
29
+ /**
30
+ * The Domain's own authority toward the source Kernel of an admitted call that carries no
31
+ * credential. No caller credential bounds it: its callbacks live as long as the execution, exactly
32
+ * as the `self` half of an authenticated call does within its carrier.
33
+ */
34
+ export declare function selfInvocation(kernel: IssuerId, execution: {
35
+ readonly deadline: number;
36
+ readonly signal: AbortSignal;
37
+ }, configuration: AuthenticationConfiguration): SelfInvocation;
29
38
  /**
30
39
  * The Domain's own authority toward one Kernel for a step of the durable Execution `executionId`,
31
40
  * which no caller credential carries: the run acts as its executor, never as its initiator. Every
@@ -78,6 +78,27 @@ export async function authenticate(credentialInput, expectedKernel, execution, c
78
78
  },
79
79
  });
80
80
  }
81
+ /**
82
+ * The Domain's own authority toward the source Kernel of an admitted call that carries no
83
+ * credential. No caller credential bounds it: its callbacks live as long as the execution, exactly
84
+ * as the `self` half of an authenticated call does within its carrier.
85
+ */
86
+ export function selfInvocation(kernel, execution, configuration) {
87
+ requireLive(execution);
88
+ const admitted = httpIssuer(kernel, configuration.policy.allowInsecureHttp === true);
89
+ admitEndpoint(invocationUrl(admitted), configuration.policy);
90
+ const session = callbackSession(admitted, selfIdentity, undefined, execution, configuration);
91
+ let closed = false;
92
+ return Object.freeze({
93
+ graph: Object.freeze({ self: bindSession(session, execution) }),
94
+ close() {
95
+ if (closed)
96
+ return;
97
+ closed = true;
98
+ session.close();
99
+ },
100
+ });
101
+ }
81
102
  /**
82
103
  * The Domain's own authority toward one Kernel for a step of the durable Execution `executionId`,
83
104
  * which no caller credential carries: the run acts as its executor, never as its initiator. Every
@@ -1,4 +1,4 @@
1
- import { admit, authenticate, executorSession } from './authentication.js';
1
+ import { admit, authenticate, executorSession, selfInvocation } from './authentication.js';
2
2
  import { releaseDelivery } from './delivery.js';
3
3
  import { httpIssuer } from './errors.js';
4
4
  import { maximumExchangeTtl, tokenExchange } from './exchange.js';
@@ -53,6 +53,7 @@ export function createIdentity(input) {
53
53
  admit: (request) => admit(request, configuration),
54
54
  executor: (kernel, executionId, execution) => executorSession(kernel, executionId, execution, configuration),
55
55
  authenticate: (request) => authenticate(request.credential, request.via.issuer, request.context.execution, configuration),
56
+ self: async (request) => selfInvocation(request.via.issuer, request.context.execution, configuration),
56
57
  });
57
58
  }
58
59
  export function identity(input) {
@@ -68,6 +69,7 @@ export function identity(input) {
68
69
  'delivery',
69
70
  'admit',
70
71
  'authenticate',
72
+ 'self',
71
73
  'executor',
72
74
  ]);
73
75
  if (Reflect.ownKeys(input).some((key) => !allowed.has(key)) ||
@@ -77,6 +79,7 @@ export function identity(input) {
77
79
  typeof input.delivery !== 'function' ||
78
80
  typeof input.admit !== 'function' ||
79
81
  typeof input.authenticate !== 'function' ||
82
+ typeof input.self !== 'function' ||
80
83
  (input.executor !== undefined && typeof input.executor !== 'function')) {
81
84
  invalid();
82
85
  }
@@ -95,6 +98,7 @@ export function identity(input) {
95
98
  delivery: receiver.delivery.bind(receiver),
96
99
  admit: receiver.admit.bind(receiver),
97
100
  authenticate: receiver.authenticate.bind(receiver),
101
+ self: receiver.self.bind(receiver),
98
102
  ...(receiver.executor === undefined ? {} : { executor: receiver.executor.bind(receiver) }),
99
103
  });
100
104
  }
@@ -1,12 +1,15 @@
1
1
  import type { HandlerRequest } from '@astrale-os/kernel-server';
2
2
  import type { AnonymousCaller, ResolvedCallable } from '../../../application/action/index.js';
3
3
  import type { Domain } from '../../../platform/schema/index.js';
4
- import type { AuthenticatedInvocation, InvocationAuthority } from './authority.js';
5
- export interface AnonymousInvocation {
4
+ import type { AuthenticatedInvocation, InvocationAuthority, SelfInvocation } from './authority.js';
5
+ /** A call without a credential: no caller, and the installed Domain's own capabilities. */
6
+ export interface AnonymousInvocation extends SelfInvocation {
6
7
  readonly caller: AnonymousCaller;
7
- readonly kernel: null;
8
- readonly graph: null;
9
- close(): void;
10
8
  }
11
9
  export type InvocationAuthentication = AnonymousInvocation | AuthenticatedInvocation;
10
+ /**
11
+ * The sessions one invocation holds toward its source Kernel: the Domain's own always, its
12
+ * caller's only when the call carried a credential.
13
+ */
14
+ export type InvocationSessions = InvocationAuthentication['graph'];
12
15
  export declare function authentication(callable: ResolvedCallable, request: HandlerRequest, domain: Domain, authority: InvocationAuthority): Promise<InvocationAuthentication>;
@@ -3,16 +3,12 @@ import { bindSession } from '../../../platform/client/session/index.js';
3
3
  import { NodeId } from '../../../platform/graph/node/index.js';
4
4
  import { protocolError } from '../result/index.js';
5
5
  import { requireVia } from './authority.js';
6
- const ANONYMOUS = Object.freeze({
7
- caller: Object.freeze({ kind: 'anonymous' }),
8
- kernel: null,
9
- graph: null,
10
- close() { },
11
- });
6
+ const ANONYMOUS = Object.freeze({ kind: 'anonymous' });
12
7
  export async function authentication(callable, request, domain, authority) {
13
8
  const credential = request.credential;
14
- if (callable.auth === 'anonymous' && credential === undefined)
15
- return ANONYMOUS;
9
+ if (callable.auth === 'anonymous' && credential === undefined) {
10
+ return anonymous(await authority.self({ via: requireVia(request), domain, context: request.context }));
11
+ }
16
12
  if (credential === undefined) {
17
13
  throw protocolError('AUTH_MISSING', 'Authentication is required.');
18
14
  }
@@ -35,7 +31,7 @@ export async function authentication(callable, request, domain, authority) {
35
31
  invalid();
36
32
  }
37
33
  const caller = admitCaller(authenticated.caller);
38
- const graphInput = admitGraph(authenticated.graph);
34
+ const graphInput = admitGraph(authenticated.graph, ['caller', 'self']);
39
35
  const graph = Object.freeze({
40
36
  caller: bindSession(graphInput.caller),
41
37
  self: bindSession(graphInput.self),
@@ -43,24 +39,46 @@ export async function authentication(callable, request, domain, authority) {
43
39
  return Object.freeze({ caller, graph, close });
44
40
  }
45
41
  catch (cause) {
46
- if (close === undefined)
47
- invalid(cause);
48
- try {
49
- close();
50
- }
51
- catch (cleanup) {
52
- throw new AggregateError([cause, cleanup], 'Invocation authority returned invalid authenticated capabilities and cleanup failed.');
42
+ rejectClosing(close, cause);
43
+ }
44
+ }
45
+ /** Admit the Domain's own capabilities for a call that carries no credential. */
46
+ function anonymous(own) {
47
+ let close;
48
+ try {
49
+ close = captureClose(own);
50
+ if (own === null ||
51
+ typeof own !== 'object' ||
52
+ Array.isArray(own) ||
53
+ Reflect.ownKeys(own).some((key) => key !== 'graph' && key !== 'close') ||
54
+ close === undefined) {
55
+ invalid();
53
56
  }
57
+ const graph = Object.freeze({ self: bindSession(admitGraph(own.graph, ['self']).self) });
58
+ return Object.freeze({ caller: ANONYMOUS, graph, close });
59
+ }
60
+ catch (cause) {
61
+ rejectClosing(close, cause);
62
+ }
63
+ }
64
+ /** Release whatever the authority opened before refusing its invalid capabilities. */
65
+ function rejectClosing(close, cause) {
66
+ if (close === undefined)
54
67
  invalid(cause);
68
+ try {
69
+ close();
70
+ }
71
+ catch (cleanup) {
72
+ throw new AggregateError([cause, cleanup], 'Invocation authority returned invalid capabilities and cleanup failed.');
55
73
  }
74
+ invalid(cause);
56
75
  }
57
- function admitGraph(input) {
76
+ function admitGraph(input, modes) {
58
77
  if (input === null ||
59
78
  typeof input !== 'object' ||
60
79
  Array.isArray(input) ||
61
- !Object.hasOwn(input, 'caller') ||
62
- !Object.hasOwn(input, 'self') ||
63
- Reflect.ownKeys(input).some((key) => key !== 'caller' && key !== 'self')) {
80
+ modes.some((mode) => !Object.hasOwn(input, mode)) ||
81
+ Reflect.ownKeys(input).some((key) => !modes.includes(key))) {
64
82
  invalid();
65
83
  }
66
84
  return input;
@@ -134,6 +152,6 @@ function optionalTime(input) {
134
152
  return input === undefined || (typeof input === 'number' && Number.isFinite(input));
135
153
  }
136
154
  function invalid(cause) {
137
- const message = 'Invocation authority returned invalid authenticated capabilities.';
155
+ const message = 'Invocation authority returned invalid capabilities.';
138
156
  throw cause === undefined ? new TypeError(message) : new TypeError(message, { cause });
139
157
  }
@@ -16,12 +16,22 @@ export interface InvocationAuthenticationInput {
16
16
  readonly domain: Domain;
17
17
  readonly context: InvocationContext;
18
18
  }
19
+ export interface InvocationSelfInput {
20
+ readonly via: invocation.Via;
21
+ readonly domain: Domain;
22
+ readonly context: InvocationContext;
23
+ }
19
24
  /** Authenticated capabilities before the SDK admits and derives application caller context. */
20
25
  export interface AuthenticatedInvocation {
21
26
  readonly caller: AuthenticatedCaller;
22
27
  readonly graph: Readonly<Record<'caller' | 'self', BoundClientSession>>;
23
28
  close(): void;
24
29
  }
30
+ /** The Domain's own capabilities, before the SDK admits them for a call without a credential. */
31
+ export interface SelfInvocation {
32
+ readonly graph: Readonly<Record<'self', BoundClientSession>>;
33
+ close(): void;
34
+ }
25
35
  export interface InvocationAuthority {
26
36
  /**
27
37
  * The source Kernel's `Function.admit` of a remote call: resolves to the admitted target, or
@@ -30,8 +40,14 @@ export interface InvocationAuthority {
30
40
  */
31
41
  admit(input: InvocationAdmissionInput): Promise<unknown>;
32
42
  authenticate(input: InvocationAuthenticationInput): Promise<AuthenticatedInvocation>;
43
+ /**
44
+ * The Domain's own authority toward the source Kernel, for an admitted call to an `anonymous`
45
+ * callable that carries no credential: its handler acts as the installed Domain, never as a
46
+ * caller.
47
+ */
48
+ self(input: InvocationSelfInput): Promise<SelfInvocation>;
33
49
  }
34
- /** Capture the execution Identity capability and receiver-bind both privileged operations. */
50
+ /** Capture the execution Identity capability and receiver-bind its privileged operations. */
35
51
  export declare function authority(input: InvocationAuthority): InvocationAuthority;
36
52
  /**
37
53
  * The routed witness of a remote call, already admitted by the Kernel Server against the served
@@ -1,15 +1,18 @@
1
1
  import { protocolError } from '../result/index.js';
2
- /** Capture the execution Identity capability and receiver-bind both privileged operations. */
2
+ /** Capture the execution Identity capability and receiver-bind its privileged operations. */
3
3
  export function authority(input) {
4
4
  if (input === null || typeof input !== 'object' || Array.isArray(input))
5
5
  invalid();
6
- if (typeof input.admit !== 'function' || typeof input.authenticate !== 'function') {
6
+ if (typeof input.admit !== 'function' ||
7
+ typeof input.authenticate !== 'function' ||
8
+ typeof input.self !== 'function') {
7
9
  invalid();
8
10
  }
9
11
  const receiver = Object.freeze({ ...input });
10
12
  return Object.freeze({
11
13
  admit: receiver.admit.bind(receiver),
12
14
  authenticate: receiver.authenticate.bind(receiver),
15
+ self: receiver.self.bind(receiver),
13
16
  });
14
17
  }
15
18
  /**
@@ -1,5 +1,5 @@
1
1
  export { authority, requireVia } from './authority.js';
2
- export type { AuthenticatedInvocation, InvocationAdmissionInput, InvocationAuthenticationInput, InvocationAuthority, } from './authority.js';
3
- export { authentication, type InvocationAuthentication } from './authentication.js';
2
+ export type { AuthenticatedInvocation, InvocationAdmissionInput, InvocationAuthenticationInput, InvocationAuthority, InvocationSelfInput, SelfInvocation, } from './authority.js';
3
+ export { authentication, type AnonymousInvocation, type InvocationAuthentication, type InvocationSessions, } from './authentication.js';
4
4
  export { input } from './input.js';
5
5
  export { target, type InvocationTarget } from './target.js';
@@ -1,4 +1,4 @@
1
1
  export { authority, requireVia } from './authority.js';
2
- export { authentication } from './authentication.js';
2
+ export { authentication, } from './authentication.js';
3
3
  export { input } from './input.js';
4
4
  export { target } from './target.js';
@@ -1,5 +1,7 @@
1
1
  import type { HandlerDependencies } from '../../../application/dependency/index.js';
2
2
  import type { BoundClientSession } from '../../../platform/client/session/index.js';
3
3
  import type { Domain } from '../../../platform/schema/index.js';
4
+ import type { InvocationSessions } from '../admission/index.js';
4
5
  /** Bind each explicit authority to its admitted session; metadata remains inert. */
5
6
  export declare function bindDependencies<D extends Domain>(domain: D, sessions: Readonly<Record<'caller' | 'self', BoundClientSession>>): HandlerDependencies<D>;
7
+ export declare function bindDependencies<D extends Domain>(domain: D, sessions: InvocationSessions): HandlerDependencies<D> | HandlerDependencies<D, 'self'>;
@@ -3,14 +3,13 @@ import { isBinaryInput } from '@astrale-os/kernel-dsl/v1/language';
3
3
  import { reference } from '../../../platform/client/session/index.js';
4
4
  import { NodeId } from '../../../platform/graph/node/index.js';
5
5
  import { Path } from '../../../platform/graph/path/index.js';
6
- /** Bind each explicit authority to its admitted session; metadata remains inert. */
7
6
  export function bindDependencies(domain, sessions) {
8
7
  const dependencies = Object.create(null);
9
8
  for (const [alias, dependency] of Object.entries(domain.dependencies)) {
10
9
  if (dependency.origin === 'kernel.astrale.ai')
11
10
  continue;
12
11
  dependencies[alias] = Object.freeze({
13
- caller: bindDependency(dependency, sessions.caller),
12
+ ...('caller' in sessions ? { caller: bindDependency(dependency, sessions.caller) } : {}),
14
13
  self: bindDependency(dependency, sessions.self),
15
14
  });
16
15
  }
@@ -12,11 +12,11 @@ export interface DispatchContext {
12
12
  readonly input: unknown;
13
13
  readonly body?: ActionBinaryInput;
14
14
  readonly caller: Caller;
15
- readonly dependencies: HandlerDependencies<Domain> | null;
16
- readonly kernel: DomainKernelSessions | null;
17
- readonly graph: DomainGraphExecutors | null;
18
- readonly query?: QueryExecutor;
19
- readonly mutate?: MutationExecutor;
15
+ readonly dependencies: HandlerDependencies<Domain> | HandlerDependencies<Domain, 'self'>;
16
+ readonly kernel: DomainKernelSessions | DomainKernelSessions<'self'>;
17
+ readonly graph: DomainGraphExecutors | DomainGraphExecutors<Domain, 'self'>;
18
+ readonly query: QueryExecutor;
19
+ readonly mutate: MutationExecutor;
20
20
  readonly integrations: Readonly<Record<string, unknown>>;
21
21
  readonly execution: ActionExecution;
22
22
  readonly self?: NodeId;
@@ -3,7 +3,8 @@ import { bindQuery } from '../../../application/query/execution/executor.js';
3
3
  import { bindDependencies } from '../dependency/index.js';
4
4
  import { clients } from '../integration/index.js';
5
5
  export function context(request, runtime, authentication, self) {
6
- const graph = authentication.graph === null ? null : bindGraph(authentication.graph, runtime.loaded.domain);
6
+ const sessions = authentication.graph;
7
+ const graph = bindGraph(sessions, runtime.loaded.domain);
7
8
  const execution = Object.freeze({
8
9
  signal: request.context.execution.signal,
9
10
  deadline: request.context.execution.deadline,
@@ -19,46 +20,31 @@ export function context(request, runtime, authentication, self) {
19
20
  input: request.call.input,
20
21
  ...(request.context.binary === undefined ? {} : { body: request.context.binary }),
21
22
  caller: authentication.caller,
22
- kernel: authentication.graph === null
23
- ? null
24
- : Object.freeze({
25
- ...authentication.graph.self,
26
- ...authentication.graph,
27
- }),
28
- dependencies: authentication.graph === null
29
- ? null
30
- : bindDependencies(runtime.loaded.domain, authentication.graph),
23
+ kernel: Object.freeze({ ...sessions.self, ...sessions }),
24
+ dependencies: bindDependencies(runtime.loaded.domain, sessions),
31
25
  graph,
32
- ...(graph === null
33
- ? {}
34
- : {
35
- query: graph.self.query,
36
- mutate: graph.self.mutate,
37
- }),
38
- integrations: clients(runtime, request.context, authentication.graph === null
39
- ? null
40
- : {
41
- caller: authentication.graph.caller,
42
- self: authentication.graph.self,
43
- ...(authentication.caller.authentication.expiresAt === undefined
44
- ? {}
45
- : {
46
- expiresAt: authentication.caller.authentication.expiresAt,
47
- }),
48
- }),
26
+ query: graph.self.query,
27
+ mutate: graph.self.mutate,
28
+ integrations: clients(runtime, request.context, {
29
+ ...sessions,
30
+ ...(authentication.caller.kind !== 'authenticated' ||
31
+ authentication.caller.authentication.expiresAt === undefined
32
+ ? {}
33
+ : { expiresAt: authentication.caller.authentication.expiresAt }),
34
+ }),
49
35
  execution,
50
36
  ...(self === undefined ? {} : { self }),
51
37
  });
52
38
  }
53
- function bindGraph(clients, domain) {
39
+ function bindGraph(sessions, domain) {
54
40
  const bind = (client) => Object.freeze({
55
41
  query: bindQuery(client, domain),
56
42
  mutate: bindMutation(client, domain),
57
43
  });
58
- const self = bind(clients.self);
44
+ const self = bind(sessions.self);
59
45
  return Object.freeze({
60
46
  ...self,
61
- caller: bind(clients.caller),
47
+ ...('caller' in sessions ? { caller: bind(sessions.caller) } : {}),
62
48
  self,
63
49
  });
64
50
  }
@@ -1,12 +1,8 @@
1
1
  import { declaredErrorFactory, declaredErrorOf } from '../../../application/error/index.js';
2
2
  import { protocolError } from '../result/index.js';
3
3
  export async function workflow(selected, context, runner) {
4
- if (context.caller.kind !== 'authenticated' || context.kernel === null) {
5
- throw protocolError('AUTH_MISSING', 'Workflow authentication is required.');
6
- }
7
4
  const complete = Object.freeze({
8
5
  ...context,
9
- caller: context.caller,
10
6
  error: declaredErrorFactory(selected.definition),
11
7
  });
12
8
  try {
@@ -2,7 +2,8 @@ import type { InvocationContext } from '@astrale-os/kernel-server';
2
2
  import type { IntegrationInvocationOwner } from '../../../application/integration/index.js';
3
3
  import type { BoundClientSession } from '../../../platform/client/session/index.js';
4
4
  export interface IntegrationInvocationSessions {
5
- readonly caller: BoundClientSession;
5
+ /** Absent when the call carried no credential: there is no caller to invoke for. */
6
+ readonly caller?: BoundClientSession;
6
7
  readonly self: BoundClientSession;
7
8
  /** Verified incoming carrier expiry, in Unix seconds. */
8
9
  readonly expiresAt?: number;
@@ -3,7 +3,7 @@ import { bindIntegrationAuthentication } from './authentication.js';
3
3
  /** One invocation-owned logical Integration call sequence. */
4
4
  export function calls(context, sessions = null) {
5
5
  const prefix = context.idempotencyKey ?? JSON.stringify([context.invocation.source, context.invocation.id]);
6
- const invoke = sessions?.caller.invoke.bind(sessions.caller);
6
+ const invoke = sessions?.caller?.invoke.bind(sessions.caller);
7
7
  const auth = sessions === null
8
8
  ? undefined
9
9
  : bindIntegrationAuthentication(context, sessions.self, sessions.expiresAt);
@@ -2,4 +2,4 @@ import type { InvocationContext } from '@astrale-os/kernel-server';
2
2
  import type { IntegrationClients, Integrations } from '../../../application/integration/index.js';
3
3
  import type { InitializedRuntime } from '../../runtime/index.js';
4
4
  import type { IntegrationInvocationSessions } from './call.js';
5
- export declare function clients(runtime: InitializedRuntime, context: InvocationContext, sessions: IntegrationInvocationSessions | null): IntegrationClients<Integrations>;
5
+ export declare function clients(runtime: InitializedRuntime, context: InvocationContext, sessions: IntegrationInvocationSessions): IntegrationClients<Integrations>;
@@ -16,13 +16,18 @@ export interface PublishCommandInput {
16
16
  readonly json: boolean;
17
17
  readonly signal: AbortSignal;
18
18
  }
19
- /** What `publish` uses of the Domain registry: the index, a baseline's bundle, the Publication. */
20
- type PublishRegistry = Pick<DomainRegistry, 'index' | 'bundle' | 'publish'>;
19
+ /**
20
+ * What `publish` uses of the Domain registry: the index, the claim of an origin no one has, a
21
+ * baseline's bundle, the Publication.
22
+ */
23
+ type PublishRegistry = Pick<DomainRegistry, 'index' | 'claim' | 'bundle' | 'publish'>;
21
24
  export interface PublishCommandDependencies {
22
25
  readonly open: (input: {
23
26
  readonly projectDir: string;
24
27
  readonly identity?: string;
25
28
  readonly signal: AbortSignal;
29
+ /** The first Astrale CLI release that serves every registry command publish may run. */
30
+ readonly minimum?: string;
26
31
  }) => Promise<PublishRegistry>;
27
32
  /** Step 5: the deployment serves exactly this sealed release at its URL. */
28
33
  readonly verify: (release: Release, signal: AbortSignal) => Promise<void>;
@@ -55,7 +60,9 @@ export declare const VERIFY_TIMEOUT_MS = 60000;
55
60
  * configure, placement, label, URL and the sealed release digest.
56
61
  * 1. Read the version from `package.json`.
57
62
  * 2. Read the registry. The version exists: the same release succeeds without effect, another is
58
- * refused. A Domain absent or unreadable, or a registry that does not answer, is refused.
63
+ * refused. An origin the registry does not hold for this identity is claimed: Admin gives an
64
+ * origin no one has to its first claimer, who administers its Domain, and refuses one another
65
+ * account holds. A registry that does not answer is refused.
59
66
  * 3. Judge the version against the highest stable version below it (AM-23); refuse a version
60
67
  * that is too low.
61
68
  * 4. Deploy the release, or reuse its deployment.
@@ -68,6 +75,7 @@ export declare const VERIFY_TIMEOUT_MS = 60000;
68
75
  * keeps the published deployment. No first-party adapter declares it: Admin's Services and a
69
76
  * Cloudflare dispatch namespace keep every deployment until its owner retires it.
70
77
  *
71
- * Steps 0 to 3 make no deploy call. Never installs.
78
+ * Steps 0 to 3 make no deploy call; step 2's claim is the only registry change before step 6.
79
+ * Never installs.
72
80
  */
73
81
  export declare function runPublish(input: PublishCommandInput, dependencies?: PublishCommandDependencies): Promise<number>;
@@ -7,7 +7,7 @@ import { renderDiff } from '../diff/render.js';
7
7
  import { diffReport, publishedSchema } from '../diff/report.js';
8
8
  import { deployPlannedRelease, planImmutableDeployment, runtimeCommit, } from '../immutable-deployment.js';
9
9
  import { DIM, RED, RESET, YELLOW } from '../log.js';
10
- import { RegistryCliError, openDomainRegistry } from '../registry/index.js';
10
+ import { MINIMUM_CLAIM_CLI_VERSION, RegistryCliError, openDomainRegistry, } from '../registry/index.js';
11
11
  import { publishReport } from './report.js';
12
12
  /**
13
13
  * Step 5 reads the deployment's `release.json` (at most 1 MiB) and its Bundle (at most 16 MiB)
@@ -36,7 +36,9 @@ const DEFAULT_DEPENDENCIES = Object.freeze({
36
36
  * configure, placement, label, URL and the sealed release digest.
37
37
  * 1. Read the version from `package.json`.
38
38
  * 2. Read the registry. The version exists: the same release succeeds without effect, another is
39
- * refused. A Domain absent or unreadable, or a registry that does not answer, is refused.
39
+ * refused. An origin the registry does not hold for this identity is claimed: Admin gives an
40
+ * origin no one has to its first claimer, who administers its Domain, and refuses one another
41
+ * account holds. A registry that does not answer is refused.
40
42
  * 3. Judge the version against the highest stable version below it (AM-23); refuse a version
41
43
  * that is too low.
42
44
  * 4. Deploy the release, or reuse its deployment.
@@ -49,7 +51,8 @@ const DEFAULT_DEPENDENCIES = Object.freeze({
49
51
  * keeps the published deployment. No first-party adapter declares it: Admin's Services and a
50
52
  * Cloudflare dispatch namespace keep every deployment until its owner retires it.
51
53
  *
52
- * Steps 0 to 3 make no deploy call. Never installs.
54
+ * Steps 0 to 3 make no deploy call; step 2's claim is the only registry change before step 6.
55
+ * Never installs.
53
56
  */
54
57
  export async function runPublish(input, dependencies = DEFAULT_DEPENDENCIES) {
55
58
  const { build, environment, signal } = input;
@@ -104,7 +107,12 @@ export async function runPublish(input, dependencies = DEFAULT_DEPENDENCIES) {
104
107
  let registry;
105
108
  let index;
106
109
  try {
107
- registry = await dependencies.open({ projectDir: input.projectDir, ...identity, signal });
110
+ registry = await dependencies.open({
111
+ projectDir: input.projectDir,
112
+ ...identity,
113
+ signal,
114
+ minimum: MINIMUM_CLAIM_CLI_VERSION,
115
+ });
108
116
  index = await registry.index(origin);
109
117
  }
110
118
  catch (cause) {
@@ -113,7 +121,18 @@ export async function runPublish(input, dependencies = DEFAULT_DEPENDENCIES) {
113
121
  return refuse(`Domain registry: ${cause.code}: ${cause.message}`, 'Nothing was deployed or published.');
114
122
  }
115
123
  if (index === undefined) {
116
- return refuse(`${origin} is not in the Domain registry, or you may not read it: nothing was deployed.`, 'An administrator of the Admin instance registers the Domain and names who may publish it.');
124
+ // First claim: the registry holds no Domain of this origin for this identity. Admin gives an
125
+ // origin no one has to its first claimer, before anything is deployed.
126
+ try {
127
+ await registry.claim(origin);
128
+ }
129
+ catch (cause) {
130
+ if (!(cause instanceof RegistryCliError))
131
+ throw cause;
132
+ return refuse(...notClaimed(origin, cause));
133
+ }
134
+ progress(`Claimed ${origin} in the Domain registry: this identity administers it.`);
135
+ index = { origin, publications: [] };
117
136
  }
118
137
  const existing = index.publications.find(({ version }) => version === declared);
119
138
  if (existing !== undefined) {
@@ -261,6 +280,25 @@ function notPublished(name, origin, url, cause) {
261
280
  `refusal until ${remedy(cause, origin)}.`,
262
281
  ];
263
282
  }
283
+ /** Why the origin could not be claimed for this identity; nothing was deployed in any case. */
284
+ function notClaimed(origin, cause) {
285
+ switch (cause.code) {
286
+ case 'REGISTRY_ORIGIN_CLAIMED':
287
+ return [
288
+ `${origin} is already claimed in the Domain registry, and you may not read it: nothing was deployed.`,
289
+ 'An administrator of the Domain grants you domain_admin to publish it.',
290
+ ];
291
+ case 'REGISTRY_ORIGIN_RESERVED':
292
+ return [
293
+ `${origin} is under astrale.ai, which Astrale operators register: nothing was deployed.`,
294
+ ];
295
+ default:
296
+ return [
297
+ `${origin} is not in the Domain registry and could not be claimed: ${cause.code}: ${cause.message}`,
298
+ 'Nothing was deployed or published. Rerun publish: a claim is safe to repeat.',
299
+ ];
300
+ }
301
+ }
264
302
  /** CT29: a change may have applied, or the answer could not be read. */
265
303
  function unconfirmed(error) {
266
304
  const reason = error.details?.reason;
@@ -70,6 +70,13 @@ export interface RegistryYank {
70
70
  export interface DomainRegistry {
71
71
  /** The Domain's index, or `undefined` when it is absent or not readable by the caller. */
72
72
  index(origin: string): Promise<RegistryIndex | undefined>;
73
+ /**
74
+ * Claim one origin: Admin gives an origin no one has to its first claimer, who administers its
75
+ * Domain. Idempotent for a caller who administers the Domain, so a rerun is safe. An origin
76
+ * another account holds is refused (`REGISTRY_ORIGIN_CLAIMED`), as is one under `astrale.ai`
77
+ * for anyone but an Astrale operator (`REGISTRY_ORIGIN_RESERVED`).
78
+ */
79
+ claim(origin: string): Promise<void>;
73
80
  /** The bundle bytes of one Publication of the index, digest and Schema identity verified. */
74
81
  bundle(origin: string, publication: RegistryPublication): Promise<Uint8Array>;
75
82
  /**
@@ -80,6 +80,14 @@ export async function openDomainRegistry(input, dependencies = {
80
80
  throw cause;
81
81
  }
82
82
  },
83
+ async claim(origin) {
84
+ const document = await command({ executable, ...machine(['__domain-registry', 'claim', origin]) }, { stdout: DOCUMENT_LIMIT, timeoutMs: CHANGE_TIMEOUT_MS, change: true });
85
+ if (document.format !== 'astrale.registry-claim-result' ||
86
+ document.version !== 1 ||
87
+ document.origin !== origin) {
88
+ throw protocolError(`Astrale CLI answered no claim result of ${origin}.`);
89
+ }
90
+ },
83
91
  async bundle(origin, publication) {
84
92
  const directory = await mkdtemp(join(tmpdir(), 'astrale-registry-'));
85
93
  try {
@@ -12,6 +12,12 @@ export declare const MINIMUM_REGISTRY_CLI_VERSION = "1.0.0-beta.128";
12
12
  * 1.0.0-beta.128 and older were released without it.
13
13
  */
14
14
  export declare const MINIMUM_INSTALLATIONS_CLI_VERSION = "1.0.0-beta.129";
15
+ /**
16
+ * The first Astrale CLI release that claims an origin (`astrale __domain-registry claim`), which
17
+ * `astrale-domain publish` does before the first version of a Domain no one has registered.
18
+ * 1.0.0-beta.136 and older were released without it.
19
+ */
20
+ export declare const MINIMUM_CLAIM_CLI_VERSION = "1.0.0-beta.137";
15
21
  export interface RegistryExecutableDependencies {
16
22
  readonly candidates: () => Promise<readonly string[]>;
17
23
  readonly run: (input: RegistryProcessInput) => Promise<RegistryProcessResult>;
@@ -17,6 +17,12 @@ export const MINIMUM_REGISTRY_CLI_VERSION = '1.0.0-beta.128';
17
17
  * 1.0.0-beta.128 and older were released without it.
18
18
  */
19
19
  export const MINIMUM_INSTALLATIONS_CLI_VERSION = '1.0.0-beta.129';
20
+ /**
21
+ * The first Astrale CLI release that claims an origin (`astrale __domain-registry claim`), which
22
+ * `astrale-domain publish` does before the first version of a Domain no one has registered.
23
+ * 1.0.0-beta.136 and older were released without it.
24
+ */
25
+ export const MINIMUM_CLAIM_CLI_VERSION = '1.0.0-beta.137';
20
26
  /**
21
27
  * Resolve one Astrale CLI for a whole command, the official installation before PATH, and admit
22
28
  * it only when it serves the registry plumbing, so an older CLI is refused by name instead of
@@ -3,7 +3,7 @@
3
3
  * registry plumbing (CT29), with the caller's own credential and the CLI's configured Admin target.
4
4
  */
5
5
  export { RegistryCliError, openDomainRegistry, type DomainRegistry, type DomainRegistryDependencies, type RegistryBundle, type RegistryDigest, type RegistryIndex, type RegistryPublication, type RegistryPublishRequest, type RegistryPublishResult, type RegistryYank, } from './client.js';
6
- export { MINIMUM_INSTALLATIONS_CLI_VERSION, MINIMUM_REGISTRY_CLI_VERSION, resolveRegistryExecutable, } from './executable.js';
6
+ export { MINIMUM_CLAIM_CLI_VERSION, MINIMUM_INSTALLATIONS_CLI_VERSION, MINIMUM_REGISTRY_CLI_VERSION, resolveRegistryExecutable, } from './executable.js';
7
7
  export { isAdminFailure } from './installations.js';
8
8
  export type { InstallationUnknownReason, InstanceInstallation, InstanceListing, } from './installations.js';
9
9
  export { RegistryProcessTimeoutError } from './process.js';
@@ -3,6 +3,6 @@
3
3
  * registry plumbing (CT29), with the caller's own credential and the CLI's configured Admin target.
4
4
  */
5
5
  export { RegistryCliError, openDomainRegistry, } from './client.js';
6
- export { MINIMUM_INSTALLATIONS_CLI_VERSION, MINIMUM_REGISTRY_CLI_VERSION, resolveRegistryExecutable, } from './executable.js';
6
+ export { MINIMUM_CLAIM_CLI_VERSION, MINIMUM_INSTALLATIONS_CLI_VERSION, MINIMUM_REGISTRY_CLI_VERSION, resolveRegistryExecutable, } from './executable.js';
7
7
  export { isAdminFailure } from './installations.js';
8
8
  export { RegistryProcessTimeoutError } from './process.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/sdk",
3
- "version": "0.6.0-beta.24",
3
+ "version": "0.6.0-beta.26",
4
4
  "description": "Schema-first SDK for defining, composing, and deploying Astrale domains",
5
5
  "keywords": [
6
6
  "astrale",
@@ -257,11 +257,11 @@
257
257
  "registry": "https://registry.npmjs.org/"
258
258
  },
259
259
  "dependencies": {
260
- "@astrale-os/kernel-client": "0.6.0-beta.87",
261
- "@astrale-os/kernel-core": "0.9.0-beta.66",
262
- "@astrale-os/kernel-dsl": "0.2.0-beta.48",
263
- "@astrale-os/kernel-protocol": "0.5.0-beta.72",
264
- "@astrale-os/kernel-server": "0.5.0-beta.74",
260
+ "@astrale-os/kernel-client": "0.6.0-beta.88",
261
+ "@astrale-os/kernel-core": "0.9.0-beta.67",
262
+ "@astrale-os/kernel-dsl": "0.2.0-beta.49",
263
+ "@astrale-os/kernel-protocol": "0.5.0-beta.73",
264
+ "@astrale-os/kernel-server": "0.5.0-beta.75",
265
265
  "@lucide/icons": "1.34.0",
266
266
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
267
267
  "cross-spawn": "7.0.6",
@@ -287,6 +287,7 @@
287
287
  "husky": "~9.1.7",
288
288
  "lint-staged": "~17.3.0",
289
289
  "mermaid": "^11.16.1",
290
+ "miniflare": "5.20260811.1-alpha",
290
291
  "oxfmt": "0.63.0",
291
292
  "oxlint": "1.78.0",
292
293
  "preact": "^10.29.8",
@@ -316,10 +317,10 @@
316
317
  "lint-staged": {
317
318
  "*.{js,cjs,mjs,ts,tsx}": [
318
319
  "oxlint --disable-nested-config --fix",
319
- "oxfmt --write"
320
+ "oxfmt --no-error-on-unmatched-pattern --write"
320
321
  ],
321
322
  "*.{json,yml,yaml}": [
322
- "oxfmt --write"
323
+ "oxfmt --no-error-on-unmatched-pattern --write"
323
324
  ]
324
325
  },
325
326
  "devEngines": {