@astrale-domains/services 0.9.0 → 0.9.1

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/README.md CHANGED
@@ -19,9 +19,11 @@ lifecycle evidence.
19
19
  origin. Its `name` is the opaque 16-character `serviceName(project, environment)`, unique among
20
20
  Services, the DNS label of `url`, `https://<name>.<routing domain>`. `state` is `active`, or
21
21
  `deleting` while its deletion retires its deployments.
22
- - **`service_owned_by`**: every Service has exactly one owner, an Identity: a user, a group or a
22
+ - **`service_owned_by`**: a Service has at most one owner, an Identity: a user, a group or a
23
23
  service account. It is chosen once, when the first deploy creates the Service, and changes only
24
- by `transfer`.
24
+ by `transfer`. The Kernel cannot restrict the deletion of an Identity
25
+ ([kernel#1498](https://github.com/astrale-os/kernel/pull/1498)): deleting the owner detaches
26
+ the Edge, and the Service survives without an owner ([below](#a-service-without-an-owner)).
25
27
  - **`Deployment` / `CloudflareDeployment`**: one immutable release of a Service, served by one
26
28
  Workers for Platforms script at its own URL, `https://<label>.<routing domain>`, where `label` is
27
29
  `<name>-<content16>` and the content covers the build digest and the configuration digest. It
@@ -62,6 +64,21 @@ own session before it reads anything: a caller who does not own the Service find
62
64
  `service_owned_by` for an owner the caller reaches through `extends_with`
63
65
  ([kernel#1138](https://github.com/astrale-os/kernel/issues/1138)).
64
66
 
67
+ ### A Service without an owner
68
+
69
+ Once its owner Identity is deleted, a Service is nobody's: no caller satisfies `OwnService` on it,
70
+ not even the Kernel root, whose `Identity.can` answers are factual. Only an authority beyond
71
+ ownership still reaches it, such as the root writing the graph directly. The handlers treat it as
72
+ a declared state:
73
+
74
+ - Its Methods find no Service (`SERVICE_NOT_FOUND`), as for any Service the caller does not own.
75
+ - A deploy of its project and environment is refused `service-owned-elsewhere`, with a message
76
+ that it has no owner and must be transferred to one first.
77
+ - `list` never lists it.
78
+ - A `transfer` or a `delete` whose caller proved it owned the Service before its owner was deleted
79
+ completes: the transfer gives the Service the owner it names, and the deletion deletes it without
80
+ an owner Edge. Each Mutation fences on the owner it read, or on the Service still having none.
81
+
65
82
  ## Methods
66
83
 
67
84
  Call them on the Admin instance (`astrale call ... --admin`). Their wire contract is
@@ -98,7 +98,7 @@ export declare const CloudflareDeployment: import("@astrale-os/sdk/schema").Auth
98
98
  kind: "retiring";
99
99
  retryAfterMs: number;
100
100
  };
101
- }, "authenticated", false, false, undefined>>;
101
+ }, "authenticated", false, false, undefined, undefined>>;
102
102
  };
103
103
  readonly policies: {
104
104
  read: () => import("@astrale-os/sdk/schema").Policy;
@@ -19,4 +19,4 @@ export declare const retireDeployment: import("@astrale-os/sdk/schema").Method<i
19
19
  message: string;
20
20
  retryAfter?: string | undefined;
21
21
  };
22
- }, "authenticated", false, false, undefined>>;
22
+ }, "authenticated", false, false, undefined, undefined>>;
@@ -119,13 +119,13 @@ export declare const CloudflareService: import("@astrale-os/sdk/schema").Authore
119
119
  } | undefined;
120
120
  };
121
121
  };
122
- }, "authenticated", true, false, undefined>>;
122
+ }, "authenticated", true, false, undefined, undefined>>;
123
123
  hosting: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<Record<string, never>, {
124
124
  readonly mode: 'value';
125
125
  readonly value: {
126
126
  routingDomain: string;
127
127
  };
128
- }, "authenticated", true, false, undefined>>;
128
+ }, "authenticated", true, false, undefined, undefined>>;
129
129
  list: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
130
130
  project?: string | undefined;
131
131
  environment?: string | undefined;
@@ -154,7 +154,7 @@ export declare const CloudflareService: import("@astrale-os/sdk/schema").Authore
154
154
  } | undefined;
155
155
  }[];
156
156
  };
157
- }, "authenticated", true, false, undefined>>;
157
+ }, "authenticated", true, false, undefined, undefined>>;
158
158
  promote: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
159
159
  label: string;
160
160
  }, {
@@ -167,7 +167,7 @@ export declare const CloudflareService: import("@astrale-os/sdk/schema").Authore
167
167
  } | {
168
168
  current: string;
169
169
  };
170
- }, "authenticated", false, false, undefined>>;
170
+ }, "authenticated", false, false, undefined, undefined>>;
171
171
  transfer: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
172
172
  owner: string;
173
173
  }, {
@@ -180,7 +180,7 @@ export declare const CloudflareService: import("@astrale-os/sdk/schema").Authore
180
180
  } | {
181
181
  owner: string;
182
182
  };
183
- }, "authenticated", false, false, undefined>>;
183
+ }, "authenticated", false, false, undefined, undefined>>;
184
184
  setSecret: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
185
185
  name: string;
186
186
  value: string;
@@ -194,11 +194,11 @@ export declare const CloudflareService: import("@astrale-os/sdk/schema").Authore
194
194
  message: string;
195
195
  retryAfter?: string | undefined;
196
196
  };
197
- }, "authenticated", false, false, undefined>>;
197
+ }, "authenticated", false, false, undefined, undefined>>;
198
198
  secrets: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<Record<string, never>, {
199
199
  readonly mode: 'value';
200
200
  readonly value: string[];
201
- }, "authenticated", false, false, undefined>>;
201
+ }, "authenticated", false, false, undefined, undefined>>;
202
202
  setSchedule: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
203
203
  crons: string[];
204
204
  }, {
@@ -209,11 +209,11 @@ export declare const CloudflareService: import("@astrale-os/sdk/schema").Authore
209
209
  kind: "rejected";
210
210
  reason: "invalid-schedule" | "service-unavailable";
211
211
  };
212
- }, "authenticated", false, false, undefined>>;
212
+ }, "authenticated", false, false, undefined, undefined>>;
213
213
  schedules: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<Record<string, never>, {
214
214
  readonly mode: 'value';
215
215
  readonly value: string[];
216
- }, "authenticated", false, false, undefined>>;
216
+ }, "authenticated", false, false, undefined, undefined>>;
217
217
  logs: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
218
218
  label?: string | undefined;
219
219
  tail?: number | undefined;
@@ -226,7 +226,7 @@ export declare const CloudflareService: import("@astrale-os/sdk/schema").Authore
226
226
  level: string;
227
227
  line: string;
228
228
  }[];
229
- }, "authenticated", false, false, undefined>>;
229
+ }, "authenticated", false, false, undefined, undefined>>;
230
230
  delete: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<Record<string, never>, {
231
231
  readonly mode: 'value';
232
232
  readonly value: {
@@ -235,7 +235,7 @@ export declare const CloudflareService: import("@astrale-os/sdk/schema").Authore
235
235
  kind: "deleting";
236
236
  retryAfterMs: number;
237
237
  };
238
- }, "authenticated", false, false, undefined>>;
238
+ }, "authenticated", false, false, undefined, undefined>>;
239
239
  };
240
240
  readonly policies: {
241
241
  read: () => import("@astrale-os/sdk/schema").Policy;
@@ -5,7 +5,7 @@ export declare const service_owned_by: import("@astrale-os/sdk/schema").Directed
5
5
  readonly properties: never;
6
6
  readonly source: {
7
7
  readonly as: "service";
8
- readonly outgoing: "1";
8
+ readonly outgoing: "0..1";
9
9
  };
10
10
  readonly target: {
11
11
  readonly as: "owner";
@@ -3,7 +3,9 @@ import { TraverseOwnedService } from '../policies/index.js';
3
3
  import { Service } from './service.js';
4
4
  export const service_owned_by = edgeClass.directed({
5
5
  description: 'The authenticated Identity that owns and may manage one Service.',
6
- source: { as: 'service', accepts: [() => Service], outgoing: '1' },
6
+ // `service` is optional: `owner` holds a Kernel Identity, whose deletion the Edge cannot
7
+ // restrict, so the Edge detaches and the Service outlives its owner (kernel#1498).
8
+ source: { as: 'service', accepts: [() => Service], outgoing: '0..1' },
7
9
  target: { as: 'owner', accepts: [K.classes.Identity], incoming: '0..*' },
8
10
  policies: { read: () => TraverseOwnedService, traverse: () => TraverseOwnedService },
9
11
  });
@@ -1,10 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  /**
3
- * One Service: the stable identity of one project in one environment, owned by one Identity,
4
- * whatever its Worker serves (an Astrale Domain release, an API, a cron job). Every deploy adds an
5
- * immutable Deployment of it, and its URL serves the one it currently points at. Its `name` is the
6
- * SDK's `serviceName(project, environment)`: the DNS label of the Service and the prefix of each
7
- * deployment's label, unique among Services, and free again once the Service is deleted.
3
+ * One Service: the stable identity of one project in one environment, owned by one Identity until
4
+ * that Identity is deleted, whatever its Worker serves (an Astrale Domain release, an API, a cron
5
+ * job). Every deploy adds an immutable Deployment of it, and its URL serves the one it currently
6
+ * points at. Its `name` is the SDK's `serviceName(project, environment)`: the DNS label of the
7
+ * Service and the prefix of each deployment's label, unique among Services, and free again once
8
+ * the Service is deleted.
8
9
  */
9
10
  export declare const Service: import("@astrale-os/sdk/schema").AuthoredNodeClass<{
10
11
  readonly abstract: true;
@@ -3,11 +3,12 @@ import { Server } from '@astrale-os/sdk/schema/icons';
3
3
  import { z } from 'zod';
4
4
  import { serviceLifecycle } from '../states/index.js';
5
5
  /**
6
- * One Service: the stable identity of one project in one environment, owned by one Identity,
7
- * whatever its Worker serves (an Astrale Domain release, an API, a cron job). Every deploy adds an
8
- * immutable Deployment of it, and its URL serves the one it currently points at. Its `name` is the
9
- * SDK's `serviceName(project, environment)`: the DNS label of the Service and the prefix of each
10
- * deployment's label, unique among Services, and free again once the Service is deleted.
6
+ * One Service: the stable identity of one project in one environment, owned by one Identity until
7
+ * that Identity is deleted, whatever its Worker serves (an Astrale Domain release, an API, a cron
8
+ * job). Every deploy adds an immutable Deployment of it, and its URL serves the one it currently
9
+ * points at. Its `name` is the SDK's `serviceName(project, environment)`: the DNS label of the
10
+ * Service and the prefix of each deployment's label, unique among Services, and free again once
11
+ * the Service is deleted.
11
12
  */
12
13
  export const Service = nodeClass({
13
14
  abstract: true,
@@ -6,4 +6,4 @@ export declare const deleteService: import("@astrale-os/sdk/schema").Method<impo
6
6
  kind: "deleting";
7
7
  retryAfterMs: number;
8
8
  };
9
- }, "authenticated", false, false, undefined>>;
9
+ }, "authenticated", false, false, undefined, undefined>>;
@@ -96,4 +96,4 @@ export declare const deployService: import("@astrale-os/sdk/schema").Method<impo
96
96
  } | undefined;
97
97
  };
98
98
  };
99
- }, "authenticated", true, false, undefined>>;
99
+ }, "authenticated", true, false, undefined, undefined>>;
@@ -4,4 +4,4 @@ export declare const describeHosting: import("@astrale-os/sdk/schema").Method<im
4
4
  readonly value: {
5
5
  routingDomain: string;
6
6
  };
7
- }, "authenticated", true, false, undefined>>;
7
+ }, "authenticated", true, false, undefined, undefined>>;
@@ -27,4 +27,4 @@ export declare const listServiceDeployments: import("@astrale-os/sdk/schema").Me
27
27
  } | undefined;
28
28
  }[];
29
29
  };
30
- }, "authenticated", true, false, undefined>>;
30
+ }, "authenticated", true, false, undefined, undefined>>;
@@ -10,4 +10,4 @@ export declare const listServiceLogs: import("@astrale-os/sdk/schema").Method<im
10
10
  level: string;
11
11
  line: string;
12
12
  }[];
13
- }, "authenticated", false, false, undefined>>;
13
+ }, "authenticated", false, false, undefined, undefined>>;
@@ -1,4 +1,4 @@
1
1
  export declare const listServiceSchedules: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<Record<string, never>, {
2
2
  readonly mode: 'value';
3
3
  readonly value: string[];
4
- }, "authenticated", false, false, undefined>>;
4
+ }, "authenticated", false, false, undefined, undefined>>;
@@ -1,4 +1,4 @@
1
1
  export declare const listServiceSecretNames: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<Record<string, never>, {
2
2
  readonly mode: 'value';
3
3
  readonly value: string[];
4
- }, "authenticated", false, false, undefined>>;
4
+ }, "authenticated", false, false, undefined, undefined>>;
@@ -11,4 +11,4 @@ export declare const promoteService: import("@astrale-os/sdk/schema").Method<imp
11
11
  } | {
12
12
  current: string;
13
13
  };
14
- }, "authenticated", false, false, undefined>>;
14
+ }, "authenticated", false, false, undefined, undefined>>;
@@ -8,4 +8,4 @@ export declare const replaceServiceSchedules: import("@astrale-os/sdk/schema").M
8
8
  kind: "rejected";
9
9
  reason: "invalid-schedule" | "service-unavailable";
10
10
  };
11
- }, "authenticated", false, false, undefined>>;
11
+ }, "authenticated", false, false, undefined, undefined>>;
@@ -11,4 +11,4 @@ export declare const setServiceSecret: import("@astrale-os/sdk/schema").Method<i
11
11
  } | {
12
12
  name: string;
13
13
  };
14
- }, "authenticated", false, false, undefined>>;
14
+ }, "authenticated", false, false, undefined, undefined>>;
@@ -11,4 +11,4 @@ export declare const transferService: import("@astrale-os/sdk/schema").Method<im
11
11
  message: string;
12
12
  retryAfter?: string | undefined;
13
13
  };
14
- }, "authenticated", false, false, undefined>>;
14
+ }, "authenticated", false, false, undefined, undefined>>;
@@ -143,13 +143,13 @@ export declare const schema: import("@astrale-os/sdk/schema").AuthoredSchema<"se
143
143
  } | undefined;
144
144
  };
145
145
  };
146
- }, "authenticated", true, false, undefined>>;
146
+ }, "authenticated", true, false, undefined, undefined>>;
147
147
  hosting: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<Record<string, never>, {
148
148
  readonly mode: 'value';
149
149
  readonly value: {
150
150
  routingDomain: string;
151
151
  };
152
- }, "authenticated", true, false, undefined>>;
152
+ }, "authenticated", true, false, undefined, undefined>>;
153
153
  list: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
154
154
  project?: string | undefined;
155
155
  environment?: string | undefined;
@@ -178,7 +178,7 @@ export declare const schema: import("@astrale-os/sdk/schema").AuthoredSchema<"se
178
178
  } | undefined;
179
179
  }[];
180
180
  };
181
- }, "authenticated", true, false, undefined>>;
181
+ }, "authenticated", true, false, undefined, undefined>>;
182
182
  promote: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
183
183
  label: string;
184
184
  }, {
@@ -191,7 +191,7 @@ export declare const schema: import("@astrale-os/sdk/schema").AuthoredSchema<"se
191
191
  } | {
192
192
  current: string;
193
193
  };
194
- }, "authenticated", false, false, undefined>>;
194
+ }, "authenticated", false, false, undefined, undefined>>;
195
195
  transfer: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
196
196
  owner: string;
197
197
  }, {
@@ -204,7 +204,7 @@ export declare const schema: import("@astrale-os/sdk/schema").AuthoredSchema<"se
204
204
  } | {
205
205
  owner: string;
206
206
  };
207
- }, "authenticated", false, false, undefined>>;
207
+ }, "authenticated", false, false, undefined, undefined>>;
208
208
  setSecret: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
209
209
  name: string;
210
210
  value: string;
@@ -218,11 +218,11 @@ export declare const schema: import("@astrale-os/sdk/schema").AuthoredSchema<"se
218
218
  message: string;
219
219
  retryAfter?: string | undefined;
220
220
  };
221
- }, "authenticated", false, false, undefined>>;
221
+ }, "authenticated", false, false, undefined, undefined>>;
222
222
  secrets: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<Record<string, never>, {
223
223
  readonly mode: 'value';
224
224
  readonly value: string[];
225
- }, "authenticated", false, false, undefined>>;
225
+ }, "authenticated", false, false, undefined, undefined>>;
226
226
  setSchedule: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
227
227
  crons: string[];
228
228
  }, {
@@ -233,11 +233,11 @@ export declare const schema: import("@astrale-os/sdk/schema").AuthoredSchema<"se
233
233
  kind: "rejected";
234
234
  reason: "invalid-schedule" | "service-unavailable";
235
235
  };
236
- }, "authenticated", false, false, undefined>>;
236
+ }, "authenticated", false, false, undefined, undefined>>;
237
237
  schedules: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<Record<string, never>, {
238
238
  readonly mode: 'value';
239
239
  readonly value: string[];
240
- }, "authenticated", false, false, undefined>>;
240
+ }, "authenticated", false, false, undefined, undefined>>;
241
241
  logs: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<{
242
242
  label?: string | undefined;
243
243
  tail?: number | undefined;
@@ -250,7 +250,7 @@ export declare const schema: import("@astrale-os/sdk/schema").AuthoredSchema<"se
250
250
  level: string;
251
251
  line: string;
252
252
  }[];
253
- }, "authenticated", false, false, undefined>>;
253
+ }, "authenticated", false, false, undefined, undefined>>;
254
254
  delete: import("@astrale-os/sdk/schema").Method<import("@astrale-os/sdk/schema").CallableProjection<Record<string, never>, {
255
255
  readonly mode: 'value';
256
256
  readonly value: {
@@ -259,7 +259,7 @@ export declare const schema: import("@astrale-os/sdk/schema").AuthoredSchema<"se
259
259
  kind: "deleting";
260
260
  retryAfterMs: number;
261
261
  };
262
- }, "authenticated", false, false, undefined>>;
262
+ }, "authenticated", false, false, undefined, undefined>>;
263
263
  };
264
264
  readonly policies: {
265
265
  read: () => import("@astrale-os/sdk/schema").Policy;
@@ -273,7 +273,7 @@ export declare const schema: import("@astrale-os/sdk/schema").AuthoredSchema<"se
273
273
  readonly properties: never;
274
274
  readonly source: {
275
275
  readonly as: "service";
276
- readonly outgoing: "1";
276
+ readonly outgoing: "0..1";
277
277
  };
278
278
  readonly target: {
279
279
  readonly as: "owner";
@@ -465,7 +465,7 @@ export declare const schema: import("@astrale-os/sdk/schema").AuthoredSchema<"se
465
465
  kind: "retiring";
466
466
  retryAfterMs: number;
467
467
  };
468
- }, "authenticated", false, false, undefined>>;
468
+ }, "authenticated", false, false, undefined, undefined>>;
469
469
  };
470
470
  readonly policies: {
471
471
  read: () => import("@astrale-os/sdk/schema").Policy;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-domains/services",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/astrale-os/domains.git",
@@ -31,15 +31,15 @@
31
31
  "registry": "https://registry.npmjs.org/"
32
32
  },
33
33
  "dependencies": {
34
- "@astrale-os/sdk": "0.6.0-beta.31",
34
+ "@astrale-os/sdk": "0.6.0-beta.38",
35
35
  "zod": "4.4.3"
36
36
  },
37
37
  "devDependencies": {
38
- "@astrale-os/adapter-astrale": "0.5.0-beta.179",
39
- "@astrale-os/adapter-cloudflare": "0.5.0-beta.186",
38
+ "@astrale-os/adapter-astrale": "0.5.0-beta.186",
39
+ "@astrale-os/adapter-cloudflare": "0.5.0-beta.193",
40
40
  "@astrale-os/ox": ">=0.1.4 <1.0.0",
41
- "@astrale-os/shell": "0.7.0-beta.5",
42
- "@astrale-os/shell-react": "0.6.0-beta.4",
41
+ "@astrale-os/shell": "0.7.0-beta.7",
42
+ "@astrale-os/shell-react": "0.6.0-beta.8",
43
43
  "@astrale-os/ui": "1.0.0-beta.37",
44
44
  "@cloudflare/workers-types": "5.20260821.1",
45
45
  "@tailwindcss/vite": "^4.3.3",