@astrale-os/cli 0.8.1-alpha.0 → 0.8.1-alpha.2

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 (310) hide show
  1. package/dist/astrale.js +62959 -41238
  2. package/dist/public/connect-core.js +55030 -0
  3. package/dist/public/keys/index.js +45631 -0
  4. package/dist/public/paths/index.js +43990 -0
  5. package/dist/types/admin/instance/model.d.ts +40 -0
  6. package/dist/types/connect-core.d.ts +22 -0
  7. package/dist/types/connection/auth.d.ts +33 -0
  8. package/dist/types/errors.d.ts +23 -0
  9. package/dist/types/identity/registry.d.ts +25 -0
  10. package/dist/types/keys/algorithm.d.ts +6 -0
  11. package/dist/types/keys/credential.d.ts +21 -0
  12. package/dist/types/keys/index.d.ts +4 -0
  13. package/dist/types/keys/pair.d.ts +34 -0
  14. package/dist/types/lib/admin-target.d.ts +74 -0
  15. package/dist/types/lib/ca-fetch.d.ts +2 -0
  16. package/dist/types/lib/config.d.ts +71 -0
  17. package/dist/types/lib/format.d.ts +1 -0
  18. package/dist/types/lib/idp-session.d.ts +33 -0
  19. package/dist/types/lib/idp.d.ts +397 -0
  20. package/dist/types/lib/instance-target.d.ts +36 -0
  21. package/dist/types/lib/instance.d.ts +186 -0
  22. package/dist/types/lib/log.d.ts +30 -0
  23. package/dist/types/lib/login-flow.d.ts +49 -0
  24. package/dist/types/lib/meta.d.ts +36 -0
  25. package/dist/types/lib/output.d.ts +83 -0
  26. package/dist/types/lib/table.d.ts +18 -0
  27. package/dist/types/lib/validation.d.ts +11 -0
  28. package/dist/types/paths/index.d.ts +2 -0
  29. package/dist/types/state/exchange-credentials.d.ts +25 -0
  30. package/dist/types/state/files.d.ts +9 -0
  31. package/dist/types/state/identities.d.ts +42 -0
  32. package/dist/types/state/index.d.ts +8 -0
  33. package/dist/types/state/paths.d.ts +32 -0
  34. package/package.json +22 -7
  35. package/src/admin/.spec/api.d.ts +3 -0
  36. package/src/admin/.spec/architecture.md +15 -0
  37. package/src/admin/.spec/icon.svg +3 -0
  38. package/src/admin/.spec/layout.ts +6 -0
  39. package/src/admin/catalog/.spec/api.d.ts +65 -0
  40. package/src/admin/catalog/.spec/architecture.md +6 -0
  41. package/src/admin/catalog/.spec/icon.svg +3 -0
  42. package/src/admin/catalog/.spec/layout.ts +6 -0
  43. package/src/admin/catalog/__tests__/client.test.ts +171 -0
  44. package/src/admin/catalog/client.ts +214 -0
  45. package/src/admin/catalog/index.ts +13 -0
  46. package/src/admin/catalog/model.ts +42 -0
  47. package/src/admin/catalog/tsconfig.json +8 -0
  48. package/src/admin/graph/.spec/api.d.ts +28 -0
  49. package/src/admin/graph/.spec/architecture.md +5 -0
  50. package/src/admin/graph/.spec/icon.svg +3 -0
  51. package/src/admin/graph/.spec/layout.ts +3 -0
  52. package/src/admin/graph/index.ts +6 -0
  53. package/src/admin/graph/nodes.ts +56 -0
  54. package/src/admin/index.ts +2 -0
  55. package/src/admin/instance/.spec/api.d.ts +66 -0
  56. package/src/admin/instance/.spec/architecture.md +10 -0
  57. package/src/admin/instance/.spec/icon.svg +3 -0
  58. package/src/admin/instance/.spec/layout.ts +6 -0
  59. package/src/admin/instance/__tests__/client.test.ts +289 -0
  60. package/src/admin/instance/client.ts +298 -0
  61. package/src/admin/instance/index.ts +16 -0
  62. package/src/admin/instance/model.ts +65 -0
  63. package/src/admin/instance/tsconfig.json +8 -0
  64. package/src/commands/__tests__/admin-instance.test.ts +3 -36
  65. package/src/commands/__tests__/call.test.ts +21 -68
  66. package/src/commands/__tests__/domain-install-owned.test.ts +26 -27
  67. package/src/commands/__tests__/domain-list.test.ts +2 -6
  68. package/src/commands/__tests__/logs.test.ts +50 -98
  69. package/src/commands/__tests__/ls.test.ts +21 -17
  70. package/src/commands/__tests__/read-commands.test.ts +171 -101
  71. package/src/commands/__tests__/view.test.ts +100 -51
  72. package/src/commands/admin/status.ts +3 -3
  73. package/src/commands/admin/use.ts +29 -6
  74. package/src/commands/auth/login.ts +1 -1
  75. package/src/commands/auth/logout.ts +4 -2
  76. package/src/commands/auth/status.ts +2 -2
  77. package/src/commands/auth/token.ts +2 -2
  78. package/src/commands/browser.ts +1 -1
  79. package/src/commands/call.ts +55 -91
  80. package/src/commands/describe.ts +52 -147
  81. package/src/commands/domain/install.ts +81 -50
  82. package/src/commands/domain/list.ts +4 -8
  83. package/src/commands/domain/publish.ts +9 -31
  84. package/src/commands/get.ts +32 -65
  85. package/src/commands/identity/__tests__/register.test.ts +93 -0
  86. package/src/commands/identity/create.ts +2 -2
  87. package/src/commands/identity/delete.ts +2 -2
  88. package/src/commands/identity/export.ts +7 -33
  89. package/src/commands/identity/import.ts +21 -70
  90. package/src/commands/identity/list.ts +2 -2
  91. package/src/commands/identity/register.ts +148 -117
  92. package/src/commands/identity/sync.ts +2 -2
  93. package/src/commands/identity/unsync.ts +2 -2
  94. package/src/commands/identity/use.ts +2 -2
  95. package/src/commands/identity/whoami.ts +2 -2
  96. package/src/commands/idp/add.ts +1 -1
  97. package/src/commands/idp/list.ts +1 -1
  98. package/src/commands/idp/refresh.ts +1 -1
  99. package/src/commands/idp/remove.ts +2 -2
  100. package/src/commands/idp/show.ts +1 -1
  101. package/src/commands/instance/active.ts +2 -2
  102. package/src/commands/instance/bookmark.ts +8 -2
  103. package/src/commands/instance/create.ts +1 -1
  104. package/src/commands/instance/delete.ts +4 -10
  105. package/src/commands/instance/forget.ts +1 -1
  106. package/src/commands/instance/list.ts +3 -3
  107. package/src/commands/instance/status.ts +5 -7
  108. package/src/commands/instance/use.ts +3 -3
  109. package/src/commands/logs.ts +152 -279
  110. package/src/commands/ls.ts +78 -204
  111. package/src/commands/mutate.ts +70 -139
  112. package/src/commands/query.ts +100 -278
  113. package/src/commands/session/analyze.ts +1 -1
  114. package/src/commands/session/list.ts +1 -1
  115. package/src/commands/setup.ts +1 -1
  116. package/src/commands/status.ts +1 -1
  117. package/src/commands/studio.ts +1 -1
  118. package/src/commands/token.ts +26 -21
  119. package/src/commands/update.ts +1 -1
  120. package/src/commands/use.ts +2 -3
  121. package/src/commands/view-serve.ts +1 -1
  122. package/src/commands/view.ts +67 -109
  123. package/src/connect-core.test.ts +38 -0
  124. package/src/connect-core.ts +42 -0
  125. package/src/connection/.spec/api.d.ts +111 -0
  126. package/src/connection/.spec/architecture.md +41 -0
  127. package/src/connection/.spec/capabilities/connection.ts +13 -0
  128. package/src/connection/.spec/examples/call.ts +7 -0
  129. package/src/connection/.spec/flows/command.ts +20 -0
  130. package/src/connection/.spec/flows/session.ts +61 -0
  131. package/src/connection/.spec/laws/connection.ts +181 -0
  132. package/src/connection/.spec/layout.ts +19 -0
  133. package/src/{kernel → connection}/__tests__/auth.test.ts +26 -2
  134. package/src/connection/__tests__/ca-fetch.test.ts +53 -0
  135. package/src/connection/__tests__/call.test.ts +17 -0
  136. package/src/connection/__tests__/credential.test.ts +109 -0
  137. package/src/connection/__tests__/errors.test.ts +96 -0
  138. package/src/connection/__tests__/exchange.test.ts +224 -0
  139. package/src/connection/__tests__/fixtures/localhost-cert.pem +19 -0
  140. package/src/connection/__tests__/fixtures/localhost-key.base64 +1 -0
  141. package/src/connection/__tests__/managed.test.ts +39 -0
  142. package/src/connection/__tests__/self.test.ts +51 -0
  143. package/src/connection/__tests__/session.test.ts +140 -0
  144. package/src/connection/__tests__/target.test.ts +93 -0
  145. package/src/{kernel → connection}/auth.ts +8 -8
  146. package/src/connection/call.ts +7 -0
  147. package/src/{kernel/run.ts → connection/command.ts} +22 -13
  148. package/src/connection/credential.ts +114 -0
  149. package/src/{kernel → connection}/errors.ts +32 -9
  150. package/src/connection/exchange.ts +274 -0
  151. package/src/connection/index.ts +8 -0
  152. package/src/connection/self.ts +120 -0
  153. package/src/connection/session.ts +176 -0
  154. package/src/connection/target.ts +111 -0
  155. package/src/connection/tsconfig.json +6 -0
  156. package/src/graph/.spec/api.d.ts +34 -0
  157. package/src/graph/.spec/laws/documents.ts +24 -0
  158. package/src/graph/.spec/laws/projection.ts +13 -0
  159. package/src/graph/.spec/layout.ts +6 -0
  160. package/src/graph/__tests__/mutation.test.ts +43 -0
  161. package/src/graph/__tests__/projection.test.ts +22 -0
  162. package/src/graph/__tests__/query.test.ts +154 -0
  163. package/src/graph/index.ts +3 -0
  164. package/src/graph/mutation.ts +18 -0
  165. package/src/graph/projection.ts +21 -0
  166. package/src/graph/query.ts +108 -0
  167. package/src/graph/tsconfig.json +6 -0
  168. package/src/identity/.history/ard/bootstrap-root-transfer.md +57 -0
  169. package/src/identity/.history/ard/domain-mediated-registration.md +36 -0
  170. package/src/identity/.spec/api.d.ts +124 -0
  171. package/src/identity/.spec/architecture.md +33 -0
  172. package/src/identity/.spec/capabilities/identity.ts +7 -0
  173. package/src/identity/.spec/flows/bootstrap-root.ts +79 -0
  174. package/src/identity/.spec/flows/domain-mediated-registration.ts +51 -0
  175. package/src/identity/.spec/flows/import.ts +29 -0
  176. package/src/identity/.spec/laws/identity.ts +56 -0
  177. package/src/identity/.spec/layout.ts +13 -0
  178. package/src/identity/.spec/schemas/identity-export-v1.schema.json +25 -0
  179. package/src/identity/__tests__/fixtures/registry-journey.ts +53 -0
  180. package/src/identity/__tests__/registration.test.ts +64 -0
  181. package/src/identity/__tests__/registry.test.ts +70 -0
  182. package/src/identity/__tests__/transfer.test.ts +198 -0
  183. package/src/identity/index.ts +23 -0
  184. package/src/identity/registration.ts +72 -0
  185. package/src/identity/registry.ts +172 -0
  186. package/src/identity/transfer.ts +187 -0
  187. package/src/identity/tsconfig.json +6 -0
  188. package/src/index.ts +1 -0
  189. package/src/keys/.spec/api.d.ts +74 -0
  190. package/src/keys/.spec/architecture.md +13 -0
  191. package/src/keys/.spec/laws/keys.ts +43 -0
  192. package/src/keys/.spec/layout.ts +6 -0
  193. package/src/keys/__tests__/algorithm.test.ts +39 -0
  194. package/src/keys/__tests__/keys.test.ts +168 -0
  195. package/src/keys/algorithm.ts +33 -0
  196. package/src/keys/credential.ts +112 -0
  197. package/src/keys/index.ts +14 -0
  198. package/src/keys/pair.ts +219 -0
  199. package/src/keys/tsconfig.json +6 -0
  200. package/src/lib/__tests__/admin-target.test.ts +67 -7
  201. package/src/lib/__tests__/binary.test.ts +2 -3
  202. package/src/lib/__tests__/command-dx.test.ts +2 -2
  203. package/src/lib/__tests__/config.test.ts +3 -2
  204. package/src/lib/__tests__/idp.test.ts +4 -6
  205. package/src/lib/__tests__/instance-target.test.ts +7 -4
  206. package/src/lib/__tests__/local-status.test.ts +1 -1
  207. package/src/lib/__tests__/log.test.ts +31 -0
  208. package/src/lib/__tests__/meta.test.ts +55 -0
  209. package/src/lib/__tests__/sdk-deps.test.ts +1 -1
  210. package/src/lib/__tests__/self.test.ts +8 -230
  211. package/src/lib/__tests__/use-target.test.ts +1 -1
  212. package/src/lib/__tests__/view-open-intent.test.ts +78 -104
  213. package/src/lib/__tests__/view-session.test.ts +99 -0
  214. package/src/lib/admin-domain.ts +39 -25
  215. package/src/lib/admin-instance.ts +38 -43
  216. package/src/lib/admin-target.ts +67 -13
  217. package/src/lib/binary.ts +13 -34
  218. package/src/lib/browser.ts +1 -1
  219. package/src/{kernel → lib}/ca-fetch.ts +35 -27
  220. package/src/lib/config.ts +1 -1
  221. package/src/lib/idp-session.ts +1 -1
  222. package/src/lib/idp.ts +3 -8
  223. package/src/lib/instance-target.ts +12 -8
  224. package/src/lib/instance.ts +34 -2
  225. package/src/lib/local-status.ts +2 -2
  226. package/src/lib/log.ts +3 -1
  227. package/src/lib/login-flow.ts +32 -5
  228. package/src/lib/meta.ts +15 -11
  229. package/src/lib/provision-instance.ts +4 -12
  230. package/src/lib/self.ts +5 -147
  231. package/src/lib/update.ts +1 -1
  232. package/src/lib/use-target.ts +1 -1
  233. package/src/lib/validation.ts +2 -2
  234. package/src/lib/view/open-intent.ts +7 -37
  235. package/src/lib/view/port-allocation.ts +1 -1
  236. package/src/lib/view/resolve.ts +52 -45
  237. package/src/lib/view/server.ts +16 -33
  238. package/src/lib/view/session.ts +51 -22
  239. package/src/paths/.spec/api.d.ts +13 -0
  240. package/src/paths/.spec/architecture.md +5 -0
  241. package/src/paths/.spec/layout.ts +3 -0
  242. package/src/paths/index.ts +13 -0
  243. package/src/program/.spec/api.d.ts +42 -0
  244. package/src/program/.spec/architecture.md +18 -0
  245. package/src/program/.spec/laws/program.ts +29 -0
  246. package/src/program/.spec/layout.ts +14 -0
  247. package/src/program/__tests__/program.test.ts +359 -0
  248. package/src/program/build.ts +190 -0
  249. package/src/program/command.ts +42 -0
  250. package/src/program/index.ts +2 -0
  251. package/src/program/options.ts +40 -0
  252. package/src/{registry.ts → program/registry.ts} +5 -1
  253. package/src/program/tsconfig.json +6 -0
  254. package/src/setup/steps/admin.ts +18 -3
  255. package/src/setup/steps/instance.ts +1 -1
  256. package/src/state/.spec/api.d.ts +133 -0
  257. package/src/state/.spec/architecture.md +31 -0
  258. package/src/state/.spec/capabilities/state.ts +7 -0
  259. package/src/state/.spec/flows/identity-update.ts +22 -0
  260. package/src/state/.spec/laws/state.ts +73 -0
  261. package/src/state/.spec/layout.ts +14 -0
  262. package/src/state/__tests__/exchange-credentials.test.ts +188 -0
  263. package/src/state/__tests__/files.test.ts +113 -0
  264. package/src/state/__tests__/fixtures/identity-transition.ts +37 -0
  265. package/src/state/__tests__/identities.test.ts +142 -0
  266. package/src/state/__tests__/paths.test.ts +27 -0
  267. package/src/state/exchange-credentials.ts +219 -0
  268. package/src/state/files.ts +129 -0
  269. package/src/state/identities.ts +236 -0
  270. package/src/state/index.ts +29 -0
  271. package/src/state/paths.ts +61 -0
  272. package/src/state/tsconfig.json +6 -0
  273. package/src/telemetry/__tests__/redact.test.ts +3 -3
  274. package/src/telemetry/__tests__/trigger.test.ts +1 -1
  275. package/src/telemetry/settings.ts +1 -1
  276. package/src/telemetry/store.ts +1 -1
  277. package/src/test-utils.ts +1 -1
  278. package/studio/client/dist/assets/{elk-api-DVw5WDTH.js → elk-api-D0cBetPW.js} +1 -1
  279. package/studio/client/dist/assets/index-Dspir4w7.js +81 -0
  280. package/studio/client/dist/assets/{index-Dl709Ra1.js → index-bVD2KJgz.js} +4 -4
  281. package/studio/client/dist/index.html +1 -1
  282. package/studio/package.json +10 -1
  283. package/studio/server/introspect/extractor.ts +1 -1
  284. package/studio/server/state/views.test.ts +3 -7
  285. package/studio/server/state/views.ts +23 -30
  286. package/tsconfig.json +1 -1
  287. package/viewer/dist/main.js +65 -39
  288. package/src/command.ts +0 -42
  289. package/src/commands/__tests__/help-contract.test.ts +0 -164
  290. package/src/kernel/__tests__/client-owned-lookup.test.ts +0 -51
  291. package/src/kernel/__tests__/errors.test.ts +0 -43
  292. package/src/kernel/__tests__/expand.test.ts +0 -123
  293. package/src/kernel/client.ts +0 -214
  294. package/src/kernel/expand.ts +0 -192
  295. package/src/kernel/graph.ts +0 -96
  296. package/src/kernel/index.ts +0 -29
  297. package/src/kernel/options.ts +0 -22
  298. package/src/kernel/types.ts +0 -14
  299. package/src/lib/__tests__/domain-identity.test.ts +0 -60
  300. package/src/lib/__tests__/fs-atomic.test.ts +0 -104
  301. package/src/lib/__tests__/identity.test.ts +0 -79
  302. package/src/lib/__tests__/keys.test.ts +0 -129
  303. package/src/lib/domain-identity.ts +0 -49
  304. package/src/lib/env.ts +0 -49
  305. package/src/lib/fs-atomic.ts +0 -126
  306. package/src/lib/identity.ts +0 -256
  307. package/src/lib/keys.ts +0 -294
  308. package/src/lib/paths.ts +0 -11
  309. package/src/program.ts +0 -213
  310. package/studio/client/dist/assets/index-CTbkDp3z.js +0 -81
@@ -0,0 +1,181 @@
1
+ import { defineLaw } from '@astrale-os/spec/authoring'
2
+
3
+ export const CLI_CONNECTION_TARGET = defineLaw({
4
+ id: 'CLI-CONNECTION-TARGET',
5
+ statement:
6
+ 'Explicit URL, explicit instance, active bookmark, managed lookup, and Admin target selection preserve the existing precedence while returning the exact invocation URL and issuer separately.',
7
+ tests: [
8
+ {
9
+ file: '__tests__/target.test.ts',
10
+ id: 'TEST-CLI-CONNECTION-SELECTS-EXACT-TARGET',
11
+ },
12
+ ],
13
+ })
14
+
15
+ export const CLI_CONNECTION_DOMAIN_EXCHANGE = defineLaw({
16
+ id: 'CLI-CONNECTION-DOMAIN-EXCHANGE',
17
+ statement:
18
+ 'A target with an explicit Domain issuer resolves the source credential, authenticates whoami at the exact Kernel, requests explicit self attenuation for that User and Domain audience, posts the resulting envelope to the discovered standard endpoint, and returns only a Domain token bound back to the same Kernel; there is no inferred issuer or legacy broker fallback.',
19
+ tests: [
20
+ {
21
+ file: '__tests__/exchange.test.ts',
22
+ id: 'TEST-CLI-EXCHANGE-WHOAMI-DELEGATE-EXCHANGE-CACHE',
23
+ },
24
+ {
25
+ file: '__tests__/exchange.test.ts',
26
+ id: 'TEST-CLI-EXCHANGE-NO-LEGACY-FALLBACK',
27
+ },
28
+ ],
29
+ })
30
+
31
+ export const CLI_CONNECTION_HOP_CREDENTIAL = defineLaw({
32
+ id: 'CLI-CONNECTION-HOP-CREDENTIAL',
33
+ statement:
34
+ 'A source hop resolves a fresh credential only when its session-pinned issuer equals the selected source issuer; a destination hop requires the same selected resolver and returns a fresh delegation for the witnessed Publication issuer, never the source credential, with its TTL bounded strictly inside any source JWT expiry.',
35
+ tests: [
36
+ {
37
+ file: '__tests__/credential.test.ts',
38
+ id: 'TEST-CLI-CONNECTION-RESOLVES-CREDENTIAL-PER-HOP',
39
+ },
40
+ {
41
+ file: '__tests__/credential.test.ts',
42
+ id: 'TEST-CLI-CONNECTION-DELEGATES-VIA-SOURCE-AUTH',
43
+ },
44
+ {
45
+ file: '__tests__/credential.test.ts',
46
+ id: 'TEST-CLI-CONNECTION-REJECTS-HOP-SOURCE-ISSUER-MISMATCH',
47
+ },
48
+ {
49
+ file: '__tests__/credential.test.ts',
50
+ id: 'TEST-CLI-CONNECTION-BOUNDS-DELEGATION-TO-SOURCE-EXPIRY',
51
+ },
52
+ ],
53
+ })
54
+
55
+ export const CLI_CONNECTION_EXPLICIT_ANONYMOUS = defineLaw({
56
+ id: 'CLI-CONNECTION-EXPLICIT-ANONYMOUS',
57
+ statement:
58
+ 'An explicit anonymous selection suppresses ambient, bookmark-default, and local credentials by omitting the Host credential capability; it is rejected before connection construction when combined with --as or --creds.',
59
+ tests: [
60
+ {
61
+ file: '__tests__/credential.test.ts',
62
+ id: 'TEST-CLI-CONNECTION-OMITS-EXPLICIT-ANONYMOUS-CREDENTIAL',
63
+ },
64
+ {
65
+ file: '__tests__/session.test.ts',
66
+ id: 'TEST-CLI-CONNECTION-REJECTS-ANONYMOUS-CREDENTIAL-CONFLICT',
67
+ },
68
+ ],
69
+ })
70
+
71
+ export const CLI_CONNECTION_SOURCE_ISSUER = defineLaw({
72
+ id: 'CLI-CONNECTION-SOURCE-ISSUER',
73
+ statement:
74
+ 'CLI constructs ClientSession once with sourceIssuer equal to the selected ConnectionTarget issuer independently from its invocation URL, so path-multiplexed Kernels do not substitute root Publication discovery; every credential hop repeats that pin.',
75
+ tests: [
76
+ {
77
+ file: '__tests__/session.test.ts',
78
+ id: 'TEST-CLI-CONNECTION-PINS-SOURCE-ISSUER',
79
+ },
80
+ ],
81
+ })
82
+
83
+ export const CLI_CONNECTION_ISSUER_DISCOVERY = defineLaw({
84
+ id: 'CLI-CONNECTION-ISSUER-DISCOVERY',
85
+ statement:
86
+ 'When invocation URL and issuer differ, liveness probes resolve OIDC metadata at the pinned issuer and reject metadata that declares any other issuer before fetching its keys.',
87
+ tests: [
88
+ {
89
+ file: '../lib/__tests__/meta.test.ts',
90
+ id: 'TEST-CLI-CONNECTION-PROBES-PINNED-ISSUER',
91
+ },
92
+ {
93
+ file: '../lib/__tests__/meta.test.ts',
94
+ id: 'TEST-CLI-CONNECTION-REJECTS-DISCOVERY-ISSUER-MISMATCH',
95
+ },
96
+ ],
97
+ })
98
+
99
+ export const CLI_AUTH_REGISTRATION_TARGET = defineLaw({
100
+ id: 'CLI-AUTH-REGISTRATION-TARGET',
101
+ statement:
102
+ 'Key identity registration lookup uses the same target key for bookmark, managed, Admin, and direct URL calls that identity registration stores.',
103
+ tests: [
104
+ {
105
+ file: '__tests__/auth.test.ts',
106
+ id: 'TEST-CLI-AUTH-USES-DIRECT-URL-REGISTRATION',
107
+ },
108
+ ],
109
+ })
110
+
111
+ export const CLI_SELF_AUTHENTICATED_PRINCIPAL = defineLaw({
112
+ id: 'CLI-SELF-AUTHENTICATED-PRINCIPAL',
113
+ statement:
114
+ '@self resolves only from authenticated Identity.whoami on the selected target, so delegated carriers use their effective principal and never an unverified JWT subject or stale local registration.',
115
+ tests: [
116
+ {
117
+ file: '__tests__/self.test.ts',
118
+ id: 'TEST-CLI-SELF-USES-AUTHENTICATED-EFFECTIVE-PRINCIPAL',
119
+ },
120
+ ],
121
+ })
122
+
123
+ export const CLI_CONNECTION_TERMINAL_CLOSE = defineLaw({
124
+ id: 'CLI-CONNECTION-TERMINAL-CLOSE',
125
+ statement:
126
+ 'The Client Session and direct source-Auth client close exactly once after action success, failure, or cancellation.',
127
+ tests: [
128
+ {
129
+ file: '__tests__/session.test.ts',
130
+ id: 'TEST-CLI-CONNECTION-CLOSES-OWNED-CLIENTS',
131
+ },
132
+ ],
133
+ })
134
+
135
+ export const CLI_CONNECTION_TIMEOUT = defineLaw({
136
+ id: 'CLI-CONNECTION-TIMEOUT',
137
+ statement:
138
+ 'The CLI accepts only a positive integer timeout before constructing a Client Session and applies it to both source-Auth and Session operations.',
139
+ tests: [
140
+ {
141
+ file: '__tests__/session.test.ts',
142
+ id: 'TEST-CLI-CONNECTION-REJECTS-INVALID-TIMEOUT-BEFORE-OPEN',
143
+ },
144
+ ],
145
+ })
146
+
147
+ export const CLI_CONNECTION_CA_SCOPE = defineLaw({
148
+ id: 'CLI-CONNECTION-CA-SCOPE',
149
+ statement:
150
+ 'A selected bookmark CA file changes only the Fetch capability owned by this connection and leaves non-HTTPS requests on the injected fallback Fetch.',
151
+ tests: [
152
+ {
153
+ file: '__tests__/ca-fetch.test.ts',
154
+ id: 'TEST-CLI-CONNECTION-SCOPES-CUSTOM-CA-TO-HTTPS-FETCH',
155
+ },
156
+ ],
157
+ })
158
+
159
+ export const CLI_CONNECTION_CALL_SHAPE = defineLaw({
160
+ id: 'CLI-CONNECTION-CALL-SHAPE',
161
+ statement:
162
+ 'Caller-authored path text and input enter Client as exactly one public Call; the CLI retains no optional callable hint, caller ETag, or second dispatch representation.',
163
+ tests: [
164
+ {
165
+ file: '__tests__/call.test.ts',
166
+ id: 'TEST-CLI-CONNECTION-CREATES-ONE-CANONICAL-CALL',
167
+ },
168
+ ],
169
+ })
170
+
171
+ export const CLI_CONNECTION_PUBLIC_SEMANTIC_REASON = defineLaw({
172
+ id: 'CLI-CONNECTION-PUBLIC-SEMANTIC-REASON',
173
+ statement:
174
+ 'Machine-mode Kernel command failures preserve the admitted ResponseError code and public semantic reason so scripts can distinguish conflicts such as revision guards and required data migrations without parsing prose.',
175
+ tests: [
176
+ {
177
+ file: '__tests__/errors.test.ts',
178
+ id: 'TEST-CLI-CONNECTION-PRESERVES-PUBLIC-SEMANTIC-REASON',
179
+ },
180
+ ],
181
+ })
@@ -0,0 +1,19 @@
1
+ import { defineLayout } from '@astrale-os/spec/authoring'
2
+
3
+ export default defineLayout({
4
+ entries: [
5
+ '__tests__/',
6
+ 'auth.ts',
7
+ 'call.ts',
8
+ 'command.ts',
9
+ 'credential.ts',
10
+ 'errors.ts',
11
+ 'exchange.ts',
12
+ 'index.ts',
13
+ 'self.ts',
14
+ 'session.ts',
15
+ 'target.ts',
16
+ 'tsconfig.json',
17
+ ],
18
+ exact: true,
19
+ })
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
2
 
3
+ import type { Identity } from '../../identity/index'
3
4
  import type { AstraleConfig } from '../../lib/config'
4
- import type { Identity } from '../../lib/identity'
5
5
 
6
6
  import { resolveKeyIdentityAuthOptions } from '../auth'
7
7
 
@@ -10,7 +10,7 @@ const config: AstraleConfig = {
10
10
  admin: {
11
11
  name: 'admin',
12
12
  url: 'https://admin.eu.astrale.ai/api',
13
- issuer: 'https://admin.eu.astrale.ai/api',
13
+ kernelIssuer: 'https://admin.eu.astrale.ai/api',
14
14
  },
15
15
  telemetry: { enabled: true },
16
16
  }
@@ -75,4 +75,28 @@ describe('resolveKeyIdentityAuthOptions', () => {
75
75
  audience: 'https://prod.example.com',
76
76
  })
77
77
  })
78
+
79
+ /** @evidence TEST-CLI-AUTH-USES-DIRECT-URL-REGISTRATION */
80
+ test('uses a registration stored for a direct URL target', () => {
81
+ const url = 'https://kernel.example/invoke'
82
+ const identity: Identity = {
83
+ subject: 'alice',
84
+ source: 'key',
85
+ mode: 'local',
86
+ createdAt: '2026-01-01T00:00:00.000Z',
87
+ registrations: {
88
+ [url]: {
89
+ iss: 'https://kernel.example/identity/alice',
90
+ sub: 'alice-node',
91
+ registeredAt: '2026-01-01T00:00:00.000Z',
92
+ },
93
+ },
94
+ }
95
+
96
+ expect(resolveKeyIdentityAuthOptions(identity, config, 'https://kernel.example', url)).toEqual({
97
+ issuer: 'https://kernel.example/identity/alice',
98
+ subject: 'alice-node',
99
+ audience: 'https://kernel.example',
100
+ })
101
+ })
78
102
  })
@@ -0,0 +1,53 @@
1
+ import { afterEach, describe, expect, test } from 'bun:test'
2
+ import { Buffer } from 'node:buffer'
3
+ import { readFile } from 'node:fs/promises'
4
+ import { fileURLToPath } from 'node:url'
5
+
6
+ import { fetchWithCaFile } from '../../lib/ca-fetch'
7
+
8
+ const certificateFile = fileURLToPath(new URL('fixtures/localhost-cert.pem', import.meta.url))
9
+ // Public test-only key material is encoded so repository secret scanners do not mistake it for a credential.
10
+ const keyFile = fileURLToPath(new URL('fixtures/localhost-key.base64', import.meta.url))
11
+ let server: Bun.Server<unknown> | undefined
12
+
13
+ afterEach(async () => {
14
+ if (server !== undefined) {
15
+ await server.stop(true)
16
+ }
17
+ server = undefined
18
+ })
19
+
20
+ describe('connection CA fetch', () => {
21
+ /** @evidence TEST-CLI-CONNECTION-SCOPES-CUSTOM-CA-TO-HTTPS-FETCH */
22
+ test('trusts the selected HTTPS CA and leaves plain HTTP on the fallback Fetch', async () => {
23
+ const [certificate, encodedKey] = await Promise.all([
24
+ readFile(certificateFile),
25
+ readFile(keyFile, 'utf8'),
26
+ ])
27
+ const key = Buffer.from(encodedKey.trim(), 'base64')
28
+ server = Bun.serve({
29
+ hostname: '127.0.0.1',
30
+ port: 0,
31
+ tls: { cert: certificate, key },
32
+ fetch: () => new Response('trusted'),
33
+ })
34
+
35
+ const requests: Array<{ input: RequestInfo | URL; init?: RequestInit }> = []
36
+ const fallback = (async (input: RequestInfo | URL, init?: RequestInit) => {
37
+ requests.push({ input, init })
38
+ return new Response('fallback')
39
+ }) as typeof fetch
40
+ const scoped = fetchWithCaFile(certificateFile, fallback)
41
+ const init = { method: 'POST', body: 'payload' } satisfies RequestInit
42
+
43
+ const httpsUrl = `https://127.0.0.1:${server.port}/invoke`
44
+ const httpsResponse = await scoped(httpsUrl)
45
+ expect(await httpsResponse.text()).toBe('trusted')
46
+ expect(httpsResponse.url).toBe(httpsUrl)
47
+ expect(httpsResponse.redirected).toBe(false)
48
+ await expect(
49
+ scoped('http://localhost:8080/invoke', init).then((value) => value.text()),
50
+ ).resolves.toBe('fallback')
51
+ expect(requests).toEqual([{ input: 'http://localhost:8080/invoke', init }])
52
+ })
53
+ })
@@ -0,0 +1,17 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+
3
+ import { createPathCall } from '../call'
4
+
5
+ describe('createPathCall', () => {
6
+ /** @evidence TEST-CLI-CONNECTION-CREATES-ONE-CANONICAL-CALL */
7
+ test('creates one path Call without legacy routing hints', () => {
8
+ expect(
9
+ JSON.parse(
10
+ JSON.stringify(createPathCall('/:kernel.astrale.ai:function.query', { value: 1 })),
11
+ ),
12
+ ).toEqual({
13
+ target: '/:kernel.astrale.ai:function.query',
14
+ input: { value: 1 },
15
+ })
16
+ })
17
+ })
@@ -0,0 +1,109 @@
1
+ import type { SessionAuth } from '@astrale-os/kernel-client/session'
2
+
3
+ import { issuer, type IssuerId } from '@astrale-os/sdk/auth'
4
+ import { Path } from '@astrale-os/sdk/graph/path'
5
+ import { describe, expect, test } from 'bun:test'
6
+
7
+ import type { AstraleConfig } from '../../lib/config'
8
+
9
+ import { createCliCredential, createConnectionCredential } from '../credential'
10
+
11
+ const SOURCE = issuer.accept('https://kernel.example')
12
+ const TARGET_CALL = Object.freeze({
13
+ target: Path.parse('/:example.dev:function.call').raw,
14
+ input: { value: 1 },
15
+ }) satisfies Parameters<SessionAuth['resolve']>[0]
16
+ const config: AstraleConfig = {
17
+ issuer: 'https://cli.example',
18
+ admin: { name: 'admin', url: SOURCE, kernelIssuer: SOURCE },
19
+ telemetry: { enabled: false },
20
+ }
21
+
22
+ describe('connection credential', () => {
23
+ /** @evidence TEST-CLI-CONNECTION-RESOLVES-SOURCE-AUTH-BEFORE-ROUTING */
24
+ test('resolves source authority without destination knowledge', async () => {
25
+ const audiences: IssuerId[] = []
26
+ const calls: unknown[] = []
27
+ const auth = createConnectionCredential(SOURCE, {
28
+ async resolve(audience) {
29
+ audiences.push(audience)
30
+ return `source:${audience}`
31
+ },
32
+ })
33
+ const signal = new AbortController().signal
34
+
35
+ calls.push(TARGET_CALL)
36
+ await expect(auth.resolve(TARGET_CALL, signal)).resolves.toEqual({
37
+ credential: `source:${SOURCE}`,
38
+ })
39
+
40
+ expect(auth.ttlSeconds).toBe(60)
41
+ expect(audiences).toEqual([SOURCE])
42
+ expect(calls).toEqual([TARGET_CALL])
43
+ })
44
+
45
+ /** @evidence TEST-CLI-CONNECTION-BOUNDS-REMOTE-CARRIER-TO-SOURCE-LIFETIME */
46
+ test('bounds destination delegation to the current source credential lifetime', async () => {
47
+ const expiresAt = Math.ceil(Date.now() / 1_000) + 120
48
+ const auth = createConnectionCredential(SOURCE, {
49
+ async resolve() {
50
+ return token(expiresAt)
51
+ },
52
+ })
53
+
54
+ const resolved = await auth.resolve(TARGET_CALL, new AbortController().signal)
55
+ expect(resolved.credential).toBe(token(expiresAt))
56
+ expect(resolved.delegate?.ttlSeconds).toBeGreaterThan(0)
57
+ expect(resolved.delegate?.ttlSeconds).toBeLessThan(120)
58
+ })
59
+
60
+ /** @evidence TEST-CLI-CONNECTION-USES-RAW-SOURCE-CREDENTIAL */
61
+ test('binds explicit CLI credentials to source-Kernel auth only', async () => {
62
+ const auth = createCliCredential(
63
+ { url: `${SOURCE}/invoke`, kernelIssuer: SOURCE, slug: 'source' },
64
+ { creds: 'raw-source-token' },
65
+ config,
66
+ )
67
+ if (auth === undefined) throw new Error('expected authenticated credential')
68
+
69
+ await expect(auth.resolve(TARGET_CALL, new AbortController().signal)).resolves.toEqual({
70
+ credential: 'raw-source-token',
71
+ })
72
+ })
73
+
74
+ /** @evidence TEST-CLI-CONNECTION-OMITS-EXPLICIT-ANONYMOUS-CREDENTIAL */
75
+ test('omits the auth capability for an explicit anonymous session', () => {
76
+ const auth = createCliCredential(
77
+ { url: `${SOURCE}/invoke`, kernelIssuer: SOURCE, defaultIdentity: 'ambient-default' },
78
+ { anonymous: true },
79
+ config,
80
+ )
81
+
82
+ expect(auth).toBeUndefined()
83
+ })
84
+
85
+ /** @evidence TEST-CLI-CONNECTION-PROPAGATES-AUTH-CANCELLATION */
86
+ test('passes the live Session operation signal to source credential resolution', async () => {
87
+ const controller = new AbortController()
88
+ const reason = new Error('cancelled')
89
+ const auth = createConnectionCredential(SOURCE, {
90
+ async resolve(_audience, signal) {
91
+ expect(signal).toBe(controller.signal)
92
+ throw signal.reason
93
+ },
94
+ })
95
+ controller.abort(reason)
96
+
97
+ await expect(auth.resolve(TARGET_CALL, controller.signal)).rejects.toBe(reason)
98
+ })
99
+ })
100
+
101
+ function token(exp: number): string {
102
+ const encode = (value: unknown) => Buffer.from(JSON.stringify(value)).toString('base64url')
103
+ return `${encode({ alg: 'EdDSA', typ: 'JWT' })}.${encode({
104
+ iss: SOURCE,
105
+ sub: 'principal',
106
+ aud: SOURCE,
107
+ exp,
108
+ })}.signature`
109
+ }
@@ -0,0 +1,96 @@
1
+ import { ResponseError } from '@astrale-os/kernel-client'
2
+ import { describe, expect, test } from 'bun:test'
3
+
4
+ import { formatKernelError, stripMethodSuffix } from '../errors'
5
+
6
+ const CONFLICT = 4001
7
+
8
+ describe('stripMethodSuffix', () => {
9
+ test('strips ::listChildren from path', () => {
10
+ expect(stripMethodSuffix('Path not found: "/nonexistent::listChildren"')).toBe(
11
+ 'Path not found: "/nonexistent"',
12
+ )
13
+ })
14
+
15
+ test('strips ::get from path', () => {
16
+ expect(stripMethodSuffix('Path not found: "/some/path::get"')).toBe(
17
+ 'Path not found: "/some/path"',
18
+ )
19
+ })
20
+
21
+ test('strips any method name', () => {
22
+ expect(stripMethodSuffix('"/kernel.astrale.ai/Root::describe"')).toBe(
23
+ '"/kernel.astrale.ai/Root"',
24
+ )
25
+ })
26
+
27
+ test('does not strip from non-path strings', () => {
28
+ expect(stripMethodSuffix('regular::text')).toBe('regular::text')
29
+ })
30
+
31
+ test('handles multiple paths in one message', () => {
32
+ expect(stripMethodSuffix('"/a/b::get" and "/c/d::list"')).toBe('"/a/b" and "/c/d"')
33
+ })
34
+
35
+ test('preserves messages without method suffixes', () => {
36
+ expect(stripMethodSuffix('No path found')).toBe('No path found')
37
+ })
38
+
39
+ test('does not strip single colon (not a method dispatch)', () => {
40
+ expect(stripMethodSuffix('"/a/b:notMethod"')).toBe('"/a/b:notMethod"')
41
+ })
42
+ })
43
+
44
+ describe('formatKernelError', () => {
45
+ /** @evidence TEST-CLI-CONNECTION-PRESERVES-PUBLIC-SEMANTIC-REASON */
46
+ test('preserves a Kernel-admitted semantic reason in machine output', async () => {
47
+ const writes: string[] = []
48
+ const original = process.stderr.write
49
+ process.stderr.write = ((chunk: string | Uint8Array) => {
50
+ writes.push(typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk))
51
+ return true
52
+ }) as typeof process.stderr.write
53
+ try {
54
+ await formatKernelError(
55
+ new ResponseError(CONFLICT, 'Schema change requires migration.', {
56
+ code: 'DATA_MIGRATION_REQUIRED',
57
+ details: {
58
+ requirements: [
59
+ {
60
+ operation: 'validate-existing',
61
+ reason: 'invalid-existing',
62
+ subject: {
63
+ kind: 'definition',
64
+ ref: { origin: 'x.test', kind: 'class', name: 'X' },
65
+ },
66
+ work: { observedFacts: 1 },
67
+ },
68
+ ],
69
+ },
70
+ }),
71
+ true,
72
+ )
73
+ } finally {
74
+ process.stderr.write = original
75
+ }
76
+ expect(writes).toHaveLength(1)
77
+ expect(JSON.parse(writes[0]!)).toEqual({
78
+ error: 'RESPONSE_ERROR',
79
+ code: CONFLICT,
80
+ message: 'Schema change requires migration.',
81
+ reason: {
82
+ code: 'DATA_MIGRATION_REQUIRED',
83
+ details: {
84
+ requirements: [
85
+ {
86
+ operation: 'validate-existing',
87
+ reason: 'invalid-existing',
88
+ subject: { kind: 'definition', ref: { origin: 'x.test', kind: 'class', name: 'X' } },
89
+ work: { observedFacts: 1 },
90
+ },
91
+ ],
92
+ },
93
+ },
94
+ })
95
+ })
96
+ })