@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.
- package/dist/astrale.js +62959 -41238
- package/dist/public/connect-core.js +55030 -0
- package/dist/public/keys/index.js +45631 -0
- package/dist/public/paths/index.js +43990 -0
- package/dist/types/admin/instance/model.d.ts +40 -0
- package/dist/types/connect-core.d.ts +22 -0
- package/dist/types/connection/auth.d.ts +33 -0
- package/dist/types/errors.d.ts +23 -0
- package/dist/types/identity/registry.d.ts +25 -0
- package/dist/types/keys/algorithm.d.ts +6 -0
- package/dist/types/keys/credential.d.ts +21 -0
- package/dist/types/keys/index.d.ts +4 -0
- package/dist/types/keys/pair.d.ts +34 -0
- package/dist/types/lib/admin-target.d.ts +74 -0
- package/dist/types/lib/ca-fetch.d.ts +2 -0
- package/dist/types/lib/config.d.ts +71 -0
- package/dist/types/lib/format.d.ts +1 -0
- package/dist/types/lib/idp-session.d.ts +33 -0
- package/dist/types/lib/idp.d.ts +397 -0
- package/dist/types/lib/instance-target.d.ts +36 -0
- package/dist/types/lib/instance.d.ts +186 -0
- package/dist/types/lib/log.d.ts +30 -0
- package/dist/types/lib/login-flow.d.ts +49 -0
- package/dist/types/lib/meta.d.ts +36 -0
- package/dist/types/lib/output.d.ts +83 -0
- package/dist/types/lib/table.d.ts +18 -0
- package/dist/types/lib/validation.d.ts +11 -0
- package/dist/types/paths/index.d.ts +2 -0
- package/dist/types/state/exchange-credentials.d.ts +25 -0
- package/dist/types/state/files.d.ts +9 -0
- package/dist/types/state/identities.d.ts +42 -0
- package/dist/types/state/index.d.ts +8 -0
- package/dist/types/state/paths.d.ts +32 -0
- package/package.json +22 -7
- package/src/admin/.spec/api.d.ts +3 -0
- package/src/admin/.spec/architecture.md +15 -0
- package/src/admin/.spec/icon.svg +3 -0
- package/src/admin/.spec/layout.ts +6 -0
- package/src/admin/catalog/.spec/api.d.ts +65 -0
- package/src/admin/catalog/.spec/architecture.md +6 -0
- package/src/admin/catalog/.spec/icon.svg +3 -0
- package/src/admin/catalog/.spec/layout.ts +6 -0
- package/src/admin/catalog/__tests__/client.test.ts +171 -0
- package/src/admin/catalog/client.ts +214 -0
- package/src/admin/catalog/index.ts +13 -0
- package/src/admin/catalog/model.ts +42 -0
- package/src/admin/catalog/tsconfig.json +8 -0
- package/src/admin/graph/.spec/api.d.ts +28 -0
- package/src/admin/graph/.spec/architecture.md +5 -0
- package/src/admin/graph/.spec/icon.svg +3 -0
- package/src/admin/graph/.spec/layout.ts +3 -0
- package/src/admin/graph/index.ts +6 -0
- package/src/admin/graph/nodes.ts +56 -0
- package/src/admin/index.ts +2 -0
- package/src/admin/instance/.spec/api.d.ts +66 -0
- package/src/admin/instance/.spec/architecture.md +10 -0
- package/src/admin/instance/.spec/icon.svg +3 -0
- package/src/admin/instance/.spec/layout.ts +6 -0
- package/src/admin/instance/__tests__/client.test.ts +289 -0
- package/src/admin/instance/client.ts +298 -0
- package/src/admin/instance/index.ts +16 -0
- package/src/admin/instance/model.ts +65 -0
- package/src/admin/instance/tsconfig.json +8 -0
- package/src/commands/__tests__/admin-instance.test.ts +3 -36
- package/src/commands/__tests__/call.test.ts +21 -68
- package/src/commands/__tests__/domain-install-owned.test.ts +26 -27
- package/src/commands/__tests__/domain-list.test.ts +2 -6
- package/src/commands/__tests__/logs.test.ts +50 -98
- package/src/commands/__tests__/ls.test.ts +21 -17
- package/src/commands/__tests__/read-commands.test.ts +171 -101
- package/src/commands/__tests__/view.test.ts +100 -51
- package/src/commands/admin/status.ts +3 -3
- package/src/commands/admin/use.ts +29 -6
- package/src/commands/auth/login.ts +1 -1
- package/src/commands/auth/logout.ts +4 -2
- package/src/commands/auth/status.ts +2 -2
- package/src/commands/auth/token.ts +2 -2
- package/src/commands/browser.ts +1 -1
- package/src/commands/call.ts +55 -91
- package/src/commands/describe.ts +52 -147
- package/src/commands/domain/install.ts +81 -50
- package/src/commands/domain/list.ts +4 -8
- package/src/commands/domain/publish.ts +9 -31
- package/src/commands/get.ts +32 -65
- package/src/commands/identity/__tests__/register.test.ts +93 -0
- package/src/commands/identity/create.ts +2 -2
- package/src/commands/identity/delete.ts +2 -2
- package/src/commands/identity/export.ts +7 -33
- package/src/commands/identity/import.ts +21 -70
- package/src/commands/identity/list.ts +2 -2
- package/src/commands/identity/register.ts +148 -117
- package/src/commands/identity/sync.ts +2 -2
- package/src/commands/identity/unsync.ts +2 -2
- package/src/commands/identity/use.ts +2 -2
- package/src/commands/identity/whoami.ts +2 -2
- package/src/commands/idp/add.ts +1 -1
- package/src/commands/idp/list.ts +1 -1
- package/src/commands/idp/refresh.ts +1 -1
- package/src/commands/idp/remove.ts +2 -2
- package/src/commands/idp/show.ts +1 -1
- package/src/commands/instance/active.ts +2 -2
- package/src/commands/instance/bookmark.ts +8 -2
- package/src/commands/instance/create.ts +1 -1
- package/src/commands/instance/delete.ts +4 -10
- package/src/commands/instance/forget.ts +1 -1
- package/src/commands/instance/list.ts +3 -3
- package/src/commands/instance/status.ts +5 -7
- package/src/commands/instance/use.ts +3 -3
- package/src/commands/logs.ts +152 -279
- package/src/commands/ls.ts +78 -204
- package/src/commands/mutate.ts +70 -139
- package/src/commands/query.ts +100 -278
- package/src/commands/session/analyze.ts +1 -1
- package/src/commands/session/list.ts +1 -1
- package/src/commands/setup.ts +1 -1
- package/src/commands/status.ts +1 -1
- package/src/commands/studio.ts +1 -1
- package/src/commands/token.ts +26 -21
- package/src/commands/update.ts +1 -1
- package/src/commands/use.ts +2 -3
- package/src/commands/view-serve.ts +1 -1
- package/src/commands/view.ts +67 -109
- package/src/connect-core.test.ts +38 -0
- package/src/connect-core.ts +42 -0
- package/src/connection/.spec/api.d.ts +111 -0
- package/src/connection/.spec/architecture.md +41 -0
- package/src/connection/.spec/capabilities/connection.ts +13 -0
- package/src/connection/.spec/examples/call.ts +7 -0
- package/src/connection/.spec/flows/command.ts +20 -0
- package/src/connection/.spec/flows/session.ts +61 -0
- package/src/connection/.spec/laws/connection.ts +181 -0
- package/src/connection/.spec/layout.ts +19 -0
- package/src/{kernel → connection}/__tests__/auth.test.ts +26 -2
- package/src/connection/__tests__/ca-fetch.test.ts +53 -0
- package/src/connection/__tests__/call.test.ts +17 -0
- package/src/connection/__tests__/credential.test.ts +109 -0
- package/src/connection/__tests__/errors.test.ts +96 -0
- package/src/connection/__tests__/exchange.test.ts +224 -0
- package/src/connection/__tests__/fixtures/localhost-cert.pem +19 -0
- package/src/connection/__tests__/fixtures/localhost-key.base64 +1 -0
- package/src/connection/__tests__/managed.test.ts +39 -0
- package/src/connection/__tests__/self.test.ts +51 -0
- package/src/connection/__tests__/session.test.ts +140 -0
- package/src/connection/__tests__/target.test.ts +93 -0
- package/src/{kernel → connection}/auth.ts +8 -8
- package/src/connection/call.ts +7 -0
- package/src/{kernel/run.ts → connection/command.ts} +22 -13
- package/src/connection/credential.ts +114 -0
- package/src/{kernel → connection}/errors.ts +32 -9
- package/src/connection/exchange.ts +274 -0
- package/src/connection/index.ts +8 -0
- package/src/connection/self.ts +120 -0
- package/src/connection/session.ts +176 -0
- package/src/connection/target.ts +111 -0
- package/src/connection/tsconfig.json +6 -0
- package/src/graph/.spec/api.d.ts +34 -0
- package/src/graph/.spec/laws/documents.ts +24 -0
- package/src/graph/.spec/laws/projection.ts +13 -0
- package/src/graph/.spec/layout.ts +6 -0
- package/src/graph/__tests__/mutation.test.ts +43 -0
- package/src/graph/__tests__/projection.test.ts +22 -0
- package/src/graph/__tests__/query.test.ts +154 -0
- package/src/graph/index.ts +3 -0
- package/src/graph/mutation.ts +18 -0
- package/src/graph/projection.ts +21 -0
- package/src/graph/query.ts +108 -0
- package/src/graph/tsconfig.json +6 -0
- package/src/identity/.history/ard/bootstrap-root-transfer.md +57 -0
- package/src/identity/.history/ard/domain-mediated-registration.md +36 -0
- package/src/identity/.spec/api.d.ts +124 -0
- package/src/identity/.spec/architecture.md +33 -0
- package/src/identity/.spec/capabilities/identity.ts +7 -0
- package/src/identity/.spec/flows/bootstrap-root.ts +79 -0
- package/src/identity/.spec/flows/domain-mediated-registration.ts +51 -0
- package/src/identity/.spec/flows/import.ts +29 -0
- package/src/identity/.spec/laws/identity.ts +56 -0
- package/src/identity/.spec/layout.ts +13 -0
- package/src/identity/.spec/schemas/identity-export-v1.schema.json +25 -0
- package/src/identity/__tests__/fixtures/registry-journey.ts +53 -0
- package/src/identity/__tests__/registration.test.ts +64 -0
- package/src/identity/__tests__/registry.test.ts +70 -0
- package/src/identity/__tests__/transfer.test.ts +198 -0
- package/src/identity/index.ts +23 -0
- package/src/identity/registration.ts +72 -0
- package/src/identity/registry.ts +172 -0
- package/src/identity/transfer.ts +187 -0
- package/src/identity/tsconfig.json +6 -0
- package/src/index.ts +1 -0
- package/src/keys/.spec/api.d.ts +74 -0
- package/src/keys/.spec/architecture.md +13 -0
- package/src/keys/.spec/laws/keys.ts +43 -0
- package/src/keys/.spec/layout.ts +6 -0
- package/src/keys/__tests__/algorithm.test.ts +39 -0
- package/src/keys/__tests__/keys.test.ts +168 -0
- package/src/keys/algorithm.ts +33 -0
- package/src/keys/credential.ts +112 -0
- package/src/keys/index.ts +14 -0
- package/src/keys/pair.ts +219 -0
- package/src/keys/tsconfig.json +6 -0
- package/src/lib/__tests__/admin-target.test.ts +67 -7
- package/src/lib/__tests__/binary.test.ts +2 -3
- package/src/lib/__tests__/command-dx.test.ts +2 -2
- package/src/lib/__tests__/config.test.ts +3 -2
- package/src/lib/__tests__/idp.test.ts +4 -6
- package/src/lib/__tests__/instance-target.test.ts +7 -4
- package/src/lib/__tests__/local-status.test.ts +1 -1
- package/src/lib/__tests__/log.test.ts +31 -0
- package/src/lib/__tests__/meta.test.ts +55 -0
- package/src/lib/__tests__/sdk-deps.test.ts +1 -1
- package/src/lib/__tests__/self.test.ts +8 -230
- package/src/lib/__tests__/use-target.test.ts +1 -1
- package/src/lib/__tests__/view-open-intent.test.ts +78 -104
- package/src/lib/__tests__/view-session.test.ts +99 -0
- package/src/lib/admin-domain.ts +39 -25
- package/src/lib/admin-instance.ts +38 -43
- package/src/lib/admin-target.ts +67 -13
- package/src/lib/binary.ts +13 -34
- package/src/lib/browser.ts +1 -1
- package/src/{kernel → lib}/ca-fetch.ts +35 -27
- package/src/lib/config.ts +1 -1
- package/src/lib/idp-session.ts +1 -1
- package/src/lib/idp.ts +3 -8
- package/src/lib/instance-target.ts +12 -8
- package/src/lib/instance.ts +34 -2
- package/src/lib/local-status.ts +2 -2
- package/src/lib/log.ts +3 -1
- package/src/lib/login-flow.ts +32 -5
- package/src/lib/meta.ts +15 -11
- package/src/lib/provision-instance.ts +4 -12
- package/src/lib/self.ts +5 -147
- package/src/lib/update.ts +1 -1
- package/src/lib/use-target.ts +1 -1
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/open-intent.ts +7 -37
- package/src/lib/view/port-allocation.ts +1 -1
- package/src/lib/view/resolve.ts +52 -45
- package/src/lib/view/server.ts +16 -33
- package/src/lib/view/session.ts +51 -22
- package/src/paths/.spec/api.d.ts +13 -0
- package/src/paths/.spec/architecture.md +5 -0
- package/src/paths/.spec/layout.ts +3 -0
- package/src/paths/index.ts +13 -0
- package/src/program/.spec/api.d.ts +42 -0
- package/src/program/.spec/architecture.md +18 -0
- package/src/program/.spec/laws/program.ts +29 -0
- package/src/program/.spec/layout.ts +14 -0
- package/src/program/__tests__/program.test.ts +359 -0
- package/src/program/build.ts +190 -0
- package/src/program/command.ts +42 -0
- package/src/program/index.ts +2 -0
- package/src/program/options.ts +40 -0
- package/src/{registry.ts → program/registry.ts} +5 -1
- package/src/program/tsconfig.json +6 -0
- package/src/setup/steps/admin.ts +18 -3
- package/src/setup/steps/instance.ts +1 -1
- package/src/state/.spec/api.d.ts +133 -0
- package/src/state/.spec/architecture.md +31 -0
- package/src/state/.spec/capabilities/state.ts +7 -0
- package/src/state/.spec/flows/identity-update.ts +22 -0
- package/src/state/.spec/laws/state.ts +73 -0
- package/src/state/.spec/layout.ts +14 -0
- package/src/state/__tests__/exchange-credentials.test.ts +188 -0
- package/src/state/__tests__/files.test.ts +113 -0
- package/src/state/__tests__/fixtures/identity-transition.ts +37 -0
- package/src/state/__tests__/identities.test.ts +142 -0
- package/src/state/__tests__/paths.test.ts +27 -0
- package/src/state/exchange-credentials.ts +219 -0
- package/src/state/files.ts +129 -0
- package/src/state/identities.ts +236 -0
- package/src/state/index.ts +29 -0
- package/src/state/paths.ts +61 -0
- package/src/state/tsconfig.json +6 -0
- package/src/telemetry/__tests__/redact.test.ts +3 -3
- package/src/telemetry/__tests__/trigger.test.ts +1 -1
- package/src/telemetry/settings.ts +1 -1
- package/src/telemetry/store.ts +1 -1
- package/src/test-utils.ts +1 -1
- package/studio/client/dist/assets/{elk-api-DVw5WDTH.js → elk-api-D0cBetPW.js} +1 -1
- package/studio/client/dist/assets/index-Dspir4w7.js +81 -0
- package/studio/client/dist/assets/{index-Dl709Ra1.js → index-bVD2KJgz.js} +4 -4
- package/studio/client/dist/index.html +1 -1
- package/studio/package.json +10 -1
- package/studio/server/introspect/extractor.ts +1 -1
- package/studio/server/state/views.test.ts +3 -7
- package/studio/server/state/views.ts +23 -30
- package/tsconfig.json +1 -1
- package/viewer/dist/main.js +65 -39
- package/src/command.ts +0 -42
- package/src/commands/__tests__/help-contract.test.ts +0 -164
- package/src/kernel/__tests__/client-owned-lookup.test.ts +0 -51
- package/src/kernel/__tests__/errors.test.ts +0 -43
- package/src/kernel/__tests__/expand.test.ts +0 -123
- package/src/kernel/client.ts +0 -214
- package/src/kernel/expand.ts +0 -192
- package/src/kernel/graph.ts +0 -96
- package/src/kernel/index.ts +0 -29
- package/src/kernel/options.ts +0 -22
- package/src/kernel/types.ts +0 -14
- package/src/lib/__tests__/domain-identity.test.ts +0 -60
- package/src/lib/__tests__/fs-atomic.test.ts +0 -104
- package/src/lib/__tests__/identity.test.ts +0 -79
- package/src/lib/__tests__/keys.test.ts +0 -129
- package/src/lib/domain-identity.ts +0 -49
- package/src/lib/env.ts +0 -49
- package/src/lib/fs-atomic.ts +0 -126
- package/src/lib/identity.ts +0 -256
- package/src/lib/keys.ts +0 -294
- package/src/lib/paths.ts +0 -11
- package/src/program.ts +0 -213
- package/studio/client/dist/assets/index-CTbkDp3z.js +0 -81
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
QueryAST as QueryASTValue,
|
|
3
|
+
QueryDefinitionRef,
|
|
4
|
+
QueryDirection,
|
|
5
|
+
} from '@astrale-os/sdk/query'
|
|
6
|
+
|
|
7
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
8
|
+
import { Query, QueryAST } from '@astrale-os/sdk/query'
|
|
9
|
+
|
|
10
|
+
export interface QueryCommandInput {
|
|
11
|
+
readonly sources: readonly string[]
|
|
12
|
+
readonly definition?: string
|
|
13
|
+
readonly ast?: unknown
|
|
14
|
+
readonly edge?: string
|
|
15
|
+
readonly direction?: QueryDirection
|
|
16
|
+
readonly limit?: string
|
|
17
|
+
readonly cursor?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PreparedQuery {
|
|
21
|
+
readonly ast: QueryASTValue
|
|
22
|
+
readonly page: Readonly<{ readonly size: number; readonly after?: string }>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const DEFAULT_LIMIT = 100
|
|
26
|
+
|
|
27
|
+
/** Admit canonical Query V6 or author the intentionally small Path/Definition/one-edge subset. */
|
|
28
|
+
export function prepareQuery(input: QueryCommandInput): PreparedQuery {
|
|
29
|
+
const limit = positiveInteger(input.limit, '--limit', DEFAULT_LIMIT)
|
|
30
|
+
if (input.ast !== undefined) {
|
|
31
|
+
if (
|
|
32
|
+
input.sources.length > 0 ||
|
|
33
|
+
input.definition !== undefined ||
|
|
34
|
+
input.edge !== undefined ||
|
|
35
|
+
input.direction !== undefined
|
|
36
|
+
) {
|
|
37
|
+
throw new TypeError(
|
|
38
|
+
'--ast/--file cannot be combined with sources, --definition, --edge, or --direction',
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
return withPage(QueryAST.decode(input.ast), limit, input.cursor)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (input.sources.length === 0 && input.definition === undefined) {
|
|
45
|
+
throw new TypeError(
|
|
46
|
+
'query requires a Path source, --definition, or a canonical Query V6 document',
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
if (input.direction !== undefined && input.edge === undefined) {
|
|
50
|
+
throw new TypeError('--direction requires --edge')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const paths = input.sources.map((source) => Path.parse(source))
|
|
54
|
+
const definition = input.definition === undefined ? undefined : definitionRef(input.definition)
|
|
55
|
+
const query =
|
|
56
|
+
paths.length > 0
|
|
57
|
+
? Query.from({
|
|
58
|
+
kind: 'node',
|
|
59
|
+
paths: paths as [Path, ...Path[]],
|
|
60
|
+
...(definition === undefined ? {} : { definitions: [definition] }),
|
|
61
|
+
})
|
|
62
|
+
: Query.from({ kind: 'node', definitions: [definition!] })
|
|
63
|
+
const ast =
|
|
64
|
+
input.edge === undefined
|
|
65
|
+
? query.select({ kind: 'graph' })
|
|
66
|
+
: query
|
|
67
|
+
.expand({
|
|
68
|
+
via: [definitionRef(input.edge)],
|
|
69
|
+
direction: input.direction ?? 'outgoing',
|
|
70
|
+
})
|
|
71
|
+
.select({ kind: 'graph' })
|
|
72
|
+
return withPage(ast, limit, input.cursor)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function definitionRef(input: string): QueryDefinitionRef {
|
|
76
|
+
const path = Path.parse(input)
|
|
77
|
+
const step = path.ast.steps[0]
|
|
78
|
+
if (
|
|
79
|
+
path.ast.anchor.kind !== 'domain' ||
|
|
80
|
+
path.ast.steps.length !== 1 ||
|
|
81
|
+
step?.kind !== 'projection' ||
|
|
82
|
+
(step.projection.kind !== 'class' && step.projection.kind !== 'interface')
|
|
83
|
+
) {
|
|
84
|
+
throw new TypeError('--definition must be one canonical Class or Interface Path')
|
|
85
|
+
}
|
|
86
|
+
return Object.freeze({
|
|
87
|
+
origin: path.ast.anchor.origin,
|
|
88
|
+
kind: step.projection.kind,
|
|
89
|
+
name: step.projection.name,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function positiveInteger(raw: string | undefined, name: string, fallback: number): number {
|
|
94
|
+
if (raw === undefined) return fallback
|
|
95
|
+
if (!/^\d+$/.test(raw)) throw new TypeError(`${name} must be a positive integer`)
|
|
96
|
+
const value = Number.parseInt(raw, 10)
|
|
97
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
98
|
+
throw new TypeError(`${name} must be a positive integer`)
|
|
99
|
+
}
|
|
100
|
+
return value
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function withPage(ast: QueryASTValue, size: number, cursor: string | undefined): PreparedQuery {
|
|
104
|
+
return Object.freeze({
|
|
105
|
+
ast,
|
|
106
|
+
page: Object.freeze({ size, ...(cursor === undefined ? {} : { after: cursor }) }),
|
|
107
|
+
})
|
|
108
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Bootstrap root transfer and child authority
|
|
2
|
+
|
|
3
|
+
Status: resolved
|
|
4
|
+
Impact: high
|
|
5
|
+
Selection: identity-export-v1-and-authenticated-management-delegation
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
A production Host creates the manager Kernel's signing material before the CLI has any local state.
|
|
10
|
+
Qualification may mount that generated secret into the operator environment, but it must not
|
|
11
|
+
manufacture the CLI registry or key files. The Host producer and CLI importer previously agreed only
|
|
12
|
+
through coincident TypeScript object shapes. The root-to-child journey was also distributed across
|
|
13
|
+
Host, Runtime, Client, and CLI behavior, leaving room to confuse a manager-signed Management carrier
|
|
14
|
+
with the effective child principal.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
https://schemas.astrale.ai/cli/identity-export/1 is the portable JSON Schema for the plaintext
|
|
19
|
+
IdentityExport V1 envelope. CLI Identity owns and publishes that schema. Structural schema admission
|
|
20
|
+
does not prove a keypair: identity import additionally proves that the private and public JWKs form
|
|
21
|
+
one pair before publishing either keys or registry state.
|
|
22
|
+
|
|
23
|
+
The Host secret generator emits one schema-conforming identity.json with mode 0600. The platform
|
|
24
|
+
runner imports it through:
|
|
25
|
+
|
|
26
|
+
astrale identity import <identity.json> --name platform-root
|
|
27
|
+
|
|
28
|
+
The imported root is the manager Kernel principal. It is never provisioned or registered as an
|
|
29
|
+
application identity. The manager bookmark uses the non-reserved alias platform-manager, pins the
|
|
30
|
+
manager issuer, and selects platform-root as its default identity. Manager identity is proved by the
|
|
31
|
+
authenticated public Identity.whoami syscall, not local CLI status.
|
|
32
|
+
|
|
33
|
+
Because this credential is issued by the target Kernel itself, CLI signing carries the imported
|
|
34
|
+
subject as an already-resolved identity Grant. The external-primary `{ identity: self }` form is not
|
|
35
|
+
valid for a self-issued Kernel bearer and cannot be substituted at this boundary.
|
|
36
|
+
|
|
37
|
+
Child creation is the public receiver call
|
|
38
|
+
/:host.astrale.ai:core.manager::createInstance. Once its Operation and Instance status report a
|
|
39
|
+
ready route and issuer, the canonical child credential is minted at the manager:
|
|
40
|
+
|
|
41
|
+
astrale token -i platform-manager --for platform-root --audience <child-issuer> --raw
|
|
42
|
+
|
|
43
|
+
That token is a manager-to-child Management carrier. Its outer JWT subject names the manager
|
|
44
|
+
principal; child Runtime maps it to the child Kernel principal. Child calls therefore keep the token
|
|
45
|
+
as --creds, and @self resolves through the child's authenticated Identity.whoami. The child bookmark
|
|
46
|
+
pins the child issuer and carries no --as platform-root default.
|
|
47
|
+
|
|
48
|
+
## Consequences
|
|
49
|
+
|
|
50
|
+
- Bootstrap crosses one explicit file contract and one supported CLI command; no CLI state file is
|
|
51
|
+
fabricated.
|
|
52
|
+
- Direct manager signing and child Management delegation remain distinct.
|
|
53
|
+
- No consumer may infer the effective principal from an unverified JWT subject or cached
|
|
54
|
+
registration.
|
|
55
|
+
- The portable schema is structural and versioned; cryptographic pair proof remains CLI admission.
|
|
56
|
+
- A future encrypted Host-to-CLI bootstrap format requires a new explicit deployment decision. CLI's
|
|
57
|
+
interactive compact-JWE export remains an operator transfer representation, not Host output.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Domain-mediated Identity registration
|
|
2
|
+
|
|
3
|
+
Status: resolved
|
|
4
|
+
Impact: high
|
|
5
|
+
Selection: explicit-authority-owner-callable
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
`identity register` prepares an atomic graph Node and self-proven Identity designation. Submitting
|
|
10
|
+
that request directly to Kernel Auth is correct only when the authenticated caller already holds
|
|
11
|
+
creation authority for the chosen Class. A child Kernel management principal does not thereby own
|
|
12
|
+
an installed application Domain's `Operator` Class, and production correctly denied that attempted
|
|
13
|
+
authority substitution.
|
|
14
|
+
|
|
15
|
+
The CLI must still own local key proof and target-bound registration state. Manufacturing CLI state,
|
|
16
|
+
handing application credentials to the operator, or weakening mutation authorization would cross
|
|
17
|
+
the wrong boundary.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
Registration retains direct Auth submission as its default. `--via <callablePath>` selects a second,
|
|
22
|
+
explicit submission boundary: after constructing the exact same Provision request and fingerprint-
|
|
23
|
+
bound self proof, CLI invokes the named Domain callable through the normal Host route. That callable
|
|
24
|
+
owns admission and may exercise its Domain authority to submit the request to Kernel Auth.
|
|
25
|
+
|
|
26
|
+
CLI treats the callable response as untrusted. It admits the prepared binding's issuer, subject, and
|
|
27
|
+
optional created Node ID, then stores only the target-keyed issuer and subject. It never accepts or
|
|
28
|
+
persists application authority, and it never exposes the self proof in argv or output.
|
|
29
|
+
|
|
30
|
+
## Consequences
|
|
31
|
+
|
|
32
|
+
- Kernel mutation non-amplification remains unchanged.
|
|
33
|
+
- A Domain that offers registration must declare and authorize a Provision-shaped callable.
|
|
34
|
+
- The option is explicit because choosing the authority owner is security-significant.
|
|
35
|
+
- Direct registration remains useful for Classes the selected caller genuinely owns.
|
|
36
|
+
- A failed callable or malformed response leaves CLI registration state unchanged.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { Call } from '@astrale-os/kernel-client'
|
|
2
|
+
import type { ProvisionRequest } from '@astrale-os/sdk/auth'
|
|
3
|
+
import type { LocalBinding } from '@astrale-os/sdk/graph/model'
|
|
4
|
+
import type { JWK } from 'jose'
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
Identity,
|
|
8
|
+
IdentityMode,
|
|
9
|
+
IdentityStore,
|
|
10
|
+
IdentityStoreOptions,
|
|
11
|
+
Registration,
|
|
12
|
+
} from '../../state/.spec/api.js'
|
|
13
|
+
|
|
14
|
+
export type { Identity, IdentityStore, Registration } from '../../state/.spec/api.js'
|
|
15
|
+
|
|
16
|
+
/** Filesystem coordinates used only by explicit identity transfer operations. */
|
|
17
|
+
export interface IdentityFileOptions {
|
|
18
|
+
readonly state?: IdentityStoreOptions
|
|
19
|
+
readonly keysDir?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Canonical V1 key-identity transfer envelope. */
|
|
23
|
+
export interface IdentityExport {
|
|
24
|
+
readonly version: 1
|
|
25
|
+
readonly subject: string
|
|
26
|
+
readonly mode: IdentityMode
|
|
27
|
+
readonly kid?: string
|
|
28
|
+
readonly issuer?: string
|
|
29
|
+
readonly privateJwk: JWK
|
|
30
|
+
readonly publicJwk: JWK
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface IdentityImportOptions extends IdentityFileOptions {
|
|
34
|
+
readonly name?: string
|
|
35
|
+
readonly issuer?: string
|
|
36
|
+
readonly replace?: boolean
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface IdentityRegistrationResult {
|
|
40
|
+
readonly iss: string
|
|
41
|
+
readonly sub: string
|
|
42
|
+
readonly nodeId?: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface IdentityProvisionSubmission {
|
|
46
|
+
readonly request: ProvisionRequest
|
|
47
|
+
readonly binding: LocalBinding
|
|
48
|
+
readonly via?: string
|
|
49
|
+
readonly direct: {
|
|
50
|
+
provision(request: ProvisionRequest): Promise<unknown>
|
|
51
|
+
}
|
|
52
|
+
readonly callable: {
|
|
53
|
+
call(call: Call): Promise<unknown>
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Submit one prepared request either directly or through its explicit Domain authority owner. */
|
|
58
|
+
export function submitIdentityProvision(
|
|
59
|
+
input: IdentityProvisionSubmission,
|
|
60
|
+
): Promise<IdentityRegistrationResult>
|
|
61
|
+
|
|
62
|
+
/** Admit only the exact binding the CLI prepared; remote callables remain untrusted input. */
|
|
63
|
+
export function acceptProvisionedIdentity(
|
|
64
|
+
value: unknown,
|
|
65
|
+
binding: LocalBinding,
|
|
66
|
+
): IdentityRegistrationResult
|
|
67
|
+
|
|
68
|
+
export function readIdentities(): Promise<IdentityStore>
|
|
69
|
+
|
|
70
|
+
export function createIdentity(
|
|
71
|
+
name: string,
|
|
72
|
+
options?: {
|
|
73
|
+
readonly subject?: string
|
|
74
|
+
readonly mode?: IdentityMode
|
|
75
|
+
readonly issuer?: string
|
|
76
|
+
readonly kid?: string
|
|
77
|
+
},
|
|
78
|
+
): Promise<Identity>
|
|
79
|
+
|
|
80
|
+
export function deleteIdentity(name: string): Promise<void>
|
|
81
|
+
export function setDefault(name: string): Promise<void>
|
|
82
|
+
export function getDefault(): Promise<Identity & { readonly name: string }>
|
|
83
|
+
export function getIdentity(name: string): Promise<Identity>
|
|
84
|
+
|
|
85
|
+
export function upsertIdpIdentity(
|
|
86
|
+
name: string,
|
|
87
|
+
options: {
|
|
88
|
+
readonly subject: string
|
|
89
|
+
readonly idp: string
|
|
90
|
+
readonly issuer: string
|
|
91
|
+
readonly audience?: string
|
|
92
|
+
readonly claims?: Readonly<Record<string, unknown>>
|
|
93
|
+
readonly use?: boolean
|
|
94
|
+
},
|
|
95
|
+
): Promise<Identity>
|
|
96
|
+
|
|
97
|
+
export function setRegistration(
|
|
98
|
+
name: string,
|
|
99
|
+
instanceSlug: string,
|
|
100
|
+
registration: Registration,
|
|
101
|
+
): Promise<void>
|
|
102
|
+
|
|
103
|
+
export function setIdentityMode(name: string, mode: IdentityMode): Promise<void>
|
|
104
|
+
|
|
105
|
+
/** Detect the retained compact-JWE representation without decoding it. */
|
|
106
|
+
export function isEncryptedIdentityExport(raw: string): boolean
|
|
107
|
+
|
|
108
|
+
/** Decode legacy plaintext, V1 plaintext, or compact-JWE content into one admitted V1 envelope. */
|
|
109
|
+
export function decodeIdentityExport(raw: string, passphrase?: string): Promise<IdentityExport>
|
|
110
|
+
|
|
111
|
+
/** Encode one admitted envelope as plaintext JSON or compact JWE. */
|
|
112
|
+
export function encodeIdentityExport(envelope: IdentityExport, passphrase?: string): Promise<string>
|
|
113
|
+
|
|
114
|
+
/** Read and prove one key-backed identity's transferable envelope. */
|
|
115
|
+
export function exportIdentity(name: string, options?: IdentityFileOptions): Promise<IdentityExport>
|
|
116
|
+
|
|
117
|
+
/** Import one admitted envelope after checking registry conflicts but before publishing registry state. */
|
|
118
|
+
export function importIdentity(
|
|
119
|
+
envelope: IdentityExport,
|
|
120
|
+
options?: IdentityImportOptions,
|
|
121
|
+
): Promise<Identity>
|
|
122
|
+
|
|
123
|
+
/** Atomically publish one explicit private export file with mode 0600. */
|
|
124
|
+
export function writeIdentityExport(path: string, content: string): Promise<void>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# CLI identity ownership
|
|
2
|
+
|
|
3
|
+
Identity owns local identity selection and the coordination between the State registry, per-subject
|
|
4
|
+
Keys, and IdP sessions. State owns durable registry decoding and locking; Keys owns JWK admission,
|
|
5
|
+
pair proof, and private files. Commands own prompts and presentation, not persistence mechanics.
|
|
6
|
+
|
|
7
|
+
Identity transfer accepts the retained unversioned plaintext envelope and the current V1 plaintext
|
|
8
|
+
or compact-JWE representation. Admission completes before mutation. Import checks the registry under
|
|
9
|
+
its file lock, persists the admitted keypair, and only then publishes the identity entry. Export
|
|
10
|
+
proves the stored pair and atomically writes one mode-`0600` user-selected file.
|
|
11
|
+
|
|
12
|
+
The canonical plaintext V1 structure is independently published as
|
|
13
|
+
`https://schemas.astrale.ai/cli/identity-export/1`. A production Host may emit that envelope as its
|
|
14
|
+
bootstrap root secret, but CLI Identity remains the format owner and cryptographically proves the
|
|
15
|
+
pair on import. The governing bootstrap flow imports the root as `platform-root`, authenticates it
|
|
16
|
+
directly to the `platform-manager` Kernel, and reaches a child only through a manager-minted
|
|
17
|
+
child-audience Management carrier. The carrier's outer subject is not the child's effective
|
|
18
|
+
principal; authenticated child `Identity.whoami` owns that projection.
|
|
19
|
+
|
|
20
|
+
Identity registration always prepares one self-proven atomic Provision request. Direct registration
|
|
21
|
+
submits it to Kernel Auth only when the selected caller already owns the target Class authority. An
|
|
22
|
+
explicit `--via <callable>` instead sends those exact bytes through the Domain that owns an
|
|
23
|
+
application Identity Class. The Domain callable owns admission and effect authority; CLI admits only
|
|
24
|
+
the prepared binding from the response before persisting its target-bound `(issuer, subject)`.
|
|
25
|
+
|
|
26
|
+
```mermaid
|
|
27
|
+
flowchart LR
|
|
28
|
+
C[identity command] --> D[decode or encode transfer]
|
|
29
|
+
D --> I[Identity orchestration]
|
|
30
|
+
I --> S[State registry lock]
|
|
31
|
+
I --> K[Keys pair proof and files]
|
|
32
|
+
S --> F[identities.json]
|
|
33
|
+
```
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { IdentityExport } from '../api.js'
|
|
2
|
+
|
|
3
|
+
interface Target {
|
|
4
|
+
readonly name: string
|
|
5
|
+
readonly url: string
|
|
6
|
+
readonly issuer: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface ReadyChild {
|
|
10
|
+
readonly target: Target
|
|
11
|
+
readonly operation: string
|
|
12
|
+
readonly instance: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface BootstrapAuthority {
|
|
16
|
+
/** Supported identity-import boundary; never a direct State or Keys write. */
|
|
17
|
+
importIdentity(input: {
|
|
18
|
+
readonly name: 'platform-root'
|
|
19
|
+
readonly envelope: IdentityExport
|
|
20
|
+
}): Promise<void>
|
|
21
|
+
bookmark(input: {
|
|
22
|
+
readonly target: Target
|
|
23
|
+
readonly defaultIdentity?: 'platform-root'
|
|
24
|
+
}): Promise<void>
|
|
25
|
+
/** Authenticated public Identity.whoami result on the selected target. */
|
|
26
|
+
whoami(input: {
|
|
27
|
+
readonly target: Target
|
|
28
|
+
readonly as?: 'platform-root'
|
|
29
|
+
readonly credential?: string
|
|
30
|
+
}): Promise<{ readonly id: string }>
|
|
31
|
+
/** Public manager createInstance call plus Operation and Instance status polling. */
|
|
32
|
+
callCreateChild(input: {
|
|
33
|
+
readonly manager: Target
|
|
34
|
+
readonly as: 'platform-root'
|
|
35
|
+
readonly operationId: string
|
|
36
|
+
readonly slug: string
|
|
37
|
+
}): Promise<ReadyChild>
|
|
38
|
+
/** Public Auth delegation invoked on the manager after authenticated whoami. */
|
|
39
|
+
delegateSelf(input: {
|
|
40
|
+
readonly manager: Target
|
|
41
|
+
readonly as: 'platform-root'
|
|
42
|
+
readonly principal: string
|
|
43
|
+
readonly audience: string
|
|
44
|
+
}): Promise<string>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Bootstrap uses the imported root directly on the manager, then an unchanged
|
|
49
|
+
* manager-to-child Management carrier whose effective child principal is
|
|
50
|
+
* established only by the child's authenticated whoami result.
|
|
51
|
+
*/
|
|
52
|
+
export async function bootstrapRootAndChild(
|
|
53
|
+
authority: BootstrapAuthority,
|
|
54
|
+
input: {
|
|
55
|
+
readonly envelope: IdentityExport
|
|
56
|
+
readonly manager: Target & { readonly name: 'platform-manager' }
|
|
57
|
+
readonly childSlug: string
|
|
58
|
+
readonly operationId: string
|
|
59
|
+
},
|
|
60
|
+
): Promise<{ readonly managerId: string; readonly childId: string; readonly child: ReadyChild }> {
|
|
61
|
+
await authority.importIdentity({ name: 'platform-root', envelope: input.envelope })
|
|
62
|
+
await authority.bookmark({ target: input.manager, defaultIdentity: 'platform-root' })
|
|
63
|
+
const manager = await authority.whoami({ target: input.manager, as: 'platform-root' })
|
|
64
|
+
const child = await authority.callCreateChild({
|
|
65
|
+
manager: input.manager,
|
|
66
|
+
as: 'platform-root',
|
|
67
|
+
operationId: input.operationId,
|
|
68
|
+
slug: input.childSlug,
|
|
69
|
+
})
|
|
70
|
+
const credential = await authority.delegateSelf({
|
|
71
|
+
manager: input.manager,
|
|
72
|
+
as: 'platform-root',
|
|
73
|
+
principal: manager.id,
|
|
74
|
+
audience: child.target.issuer,
|
|
75
|
+
})
|
|
76
|
+
await authority.bookmark({ target: child.target })
|
|
77
|
+
const effectiveChild = await authority.whoami({ target: child.target, credential })
|
|
78
|
+
return { managerId: manager.id, childId: effectiveChild.id, child }
|
|
79
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ProvisionRequest, ProvisionResult } from '@astrale-os/sdk/auth'
|
|
2
|
+
import type { LocalBinding } from '@astrale-os/sdk/mutation'
|
|
3
|
+
|
|
4
|
+
interface RegistrationAuthority {
|
|
5
|
+
prepareSelfProven(input: {
|
|
6
|
+
readonly identity: string
|
|
7
|
+
readonly classPath: string
|
|
8
|
+
readonly properties: Readonly<Record<string, unknown>>
|
|
9
|
+
readonly kernelIssuer: string
|
|
10
|
+
}): Promise<{ readonly binding: LocalBinding; readonly request: ProvisionRequest }>
|
|
11
|
+
call(input: {
|
|
12
|
+
readonly target: string
|
|
13
|
+
readonly request: ProvisionRequest
|
|
14
|
+
}): Promise<ProvisionResult>
|
|
15
|
+
persist(input: {
|
|
16
|
+
readonly identity: string
|
|
17
|
+
readonly targetKey: string
|
|
18
|
+
readonly issuer: string
|
|
19
|
+
readonly subject: string
|
|
20
|
+
}): Promise<void>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Application Identity Classes are provisioned only through their explicit authority owner. */
|
|
24
|
+
export async function registerThroughDomain(
|
|
25
|
+
authority: RegistrationAuthority,
|
|
26
|
+
input: {
|
|
27
|
+
readonly identity: string
|
|
28
|
+
readonly classPath: string
|
|
29
|
+
readonly properties: Readonly<Record<string, unknown>>
|
|
30
|
+
readonly kernelIssuer: string
|
|
31
|
+
readonly targetKey: string
|
|
32
|
+
readonly callable: string
|
|
33
|
+
},
|
|
34
|
+
): Promise<{ readonly issuer: string; readonly subject: string; readonly nodeId?: string }> {
|
|
35
|
+
const prepared = await authority.prepareSelfProven(input)
|
|
36
|
+
const result = await authority.call({ target: input.callable, request: prepared.request })
|
|
37
|
+
const identity = result.identities[prepared.binding]
|
|
38
|
+
if (identity === undefined) throw new Error('Provision result omitted the prepared binding.')
|
|
39
|
+
await authority.persist({
|
|
40
|
+
identity: input.identity,
|
|
41
|
+
targetKey: input.targetKey,
|
|
42
|
+
issuer: identity.issuer,
|
|
43
|
+
subject: identity.subject,
|
|
44
|
+
})
|
|
45
|
+
const nodeId = result.createdNodes[prepared.binding]
|
|
46
|
+
return {
|
|
47
|
+
issuer: identity.issuer,
|
|
48
|
+
subject: identity.subject,
|
|
49
|
+
...(nodeId === undefined ? {} : { nodeId }),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Identity, IdentityExport, IdentityImportOptions, IdentityStore } from '../api.js'
|
|
2
|
+
|
|
3
|
+
declare const withIdentityLock: <Value>(transition: () => Promise<Value>) => Promise<Value>
|
|
4
|
+
declare const readLatest: () => Promise<IdentityStore>
|
|
5
|
+
declare const checkImportConflict: (
|
|
6
|
+
store: IdentityStore,
|
|
7
|
+
envelope: IdentityExport,
|
|
8
|
+
options: IdentityImportOptions,
|
|
9
|
+
) => string
|
|
10
|
+
declare const persistAdmittedKeypair: (envelope: IdentityExport) => Promise<void>
|
|
11
|
+
declare const publishIdentity: (
|
|
12
|
+
store: IdentityStore,
|
|
13
|
+
name: string,
|
|
14
|
+
envelope: IdentityExport,
|
|
15
|
+
options: IdentityImportOptions,
|
|
16
|
+
) => Promise<Identity>
|
|
17
|
+
|
|
18
|
+
/** Import performs all fallible admission before entering this ordered persistence flow. */
|
|
19
|
+
export function importIdentity(
|
|
20
|
+
envelope: IdentityExport,
|
|
21
|
+
options: IdentityImportOptions,
|
|
22
|
+
): Promise<Identity> {
|
|
23
|
+
return withIdentityLock(async () => {
|
|
24
|
+
const store = await readLatest()
|
|
25
|
+
const name = checkImportConflict(store, envelope, options)
|
|
26
|
+
await persistAdmittedKeypair(envelope)
|
|
27
|
+
return publishIdentity(store, name, envelope, options)
|
|
28
|
+
})
|
|
29
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { defineLaw } from '@astrale-os/spec/authoring'
|
|
2
|
+
|
|
3
|
+
export const CLI_IDENTITY_REGISTRY_TRANSITIONS = defineLaw({
|
|
4
|
+
id: 'CLI-IDENTITY-REGISTRY-TRANSITIONS',
|
|
5
|
+
statement:
|
|
6
|
+
'Create, selection, registration, mode change, and non-default deletion preserve one locked registry and isolated subject keypair; deleting the selected identity is rejected unchanged.',
|
|
7
|
+
tests: [{ file: '__tests__/registry.test.ts', id: 'TEST-CLI-IDENTITY-REGISTRY-JOURNEY' }],
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
export const CLI_IDENTITY_TRANSFER_ADMITTED = defineLaw({
|
|
11
|
+
id: 'CLI-IDENTITY-TRANSFER-ADMITTED',
|
|
12
|
+
statement:
|
|
13
|
+
'Malformed, unsupported-version, or cryptographically mismatched transfer content is rejected before either key files or registry state changes.',
|
|
14
|
+
tests: [{ file: '__tests__/transfer.test.ts', id: 'TEST-CLI-IDENTITY-TRANSFER-REJECTS' }],
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export const CLI_IDENTITY_TRANSFER_ROUNDTRIP = defineLaw({
|
|
18
|
+
id: 'CLI-IDENTITY-TRANSFER-ROUNDTRIP',
|
|
19
|
+
statement:
|
|
20
|
+
'Legacy plaintext and V1 plaintext or compact-JWE content converge on one V1 envelope, and explicit exports are published atomically with mode 0600.',
|
|
21
|
+
tests: [
|
|
22
|
+
{ file: '__tests__/transfer.test.ts', id: 'TEST-CLI-IDENTITY-TRANSFER-ROUNDTRIP' },
|
|
23
|
+
{ file: '__tests__/transfer.test.ts', id: 'TEST-CLI-IDENTITY-EXPORT-PRIVATE' },
|
|
24
|
+
],
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export const CLI_IDENTITY_TRANSFER_SCHEMA = defineLaw({
|
|
28
|
+
id: 'CLI-IDENTITY-TRANSFER-SCHEMA',
|
|
29
|
+
statement:
|
|
30
|
+
'Plaintext IdentityExport V1 has one independently versioned portable JSON Schema; decoding additionally proves the JWK pair before any durable mutation.',
|
|
31
|
+
tests: [
|
|
32
|
+
{
|
|
33
|
+
file: '__tests__/transfer.test.ts',
|
|
34
|
+
id: 'TEST-CLI-IDENTITY-TRANSFER-SCHEMA',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
export const CLI_IDENTITY_IMPORT_ORDERED = defineLaw({
|
|
40
|
+
id: 'CLI-IDENTITY-IMPORT-ORDERED',
|
|
41
|
+
statement:
|
|
42
|
+
'Import checks name and identity-source conflicts under the registry lock before replacing keys, then publishes the registry only after the admitted pair is durable.',
|
|
43
|
+
tests: [{ file: '__tests__/transfer.test.ts', id: 'TEST-CLI-IDENTITY-IMPORT-ORDERED' }],
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
export const CLI_IDENTITY_REGISTRATION_AUTHORITY = defineLaw({
|
|
47
|
+
id: 'CLI-IDENTITY-REGISTRATION-AUTHORITY',
|
|
48
|
+
statement:
|
|
49
|
+
'Identity registration binds one self-proven Provision request to the selected Kernel; direct submission uses caller authority, while explicit Domain-mediated submission sends the same request to the named callable and persists only its admitted prepared binding.',
|
|
50
|
+
tests: [
|
|
51
|
+
{
|
|
52
|
+
file: '__tests__/registration.test.ts',
|
|
53
|
+
id: 'TEST-CLI-IDENTITY-REGISTER-DOMAIN-MEDIATED',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.astrale.ai/cli/identity-export/1",
|
|
4
|
+
"title": "Astrale Identity Export V1",
|
|
5
|
+
"description": "Portable plaintext key-identity transfer envelope consumed by the Astrale CLI.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["version", "subject", "privateJwk", "publicJwk"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"version": { "const": 1 },
|
|
11
|
+
"subject": { "type": "string", "minLength": 1 },
|
|
12
|
+
"mode": { "enum": ["local", "remote"] },
|
|
13
|
+
"kid": { "type": "string", "minLength": 1 },
|
|
14
|
+
"issuer": { "type": "string", "format": "uri" },
|
|
15
|
+
"privateJwk": { "$ref": "#/$defs/jwk" },
|
|
16
|
+
"publicJwk": { "$ref": "#/$defs/jwk" }
|
|
17
|
+
},
|
|
18
|
+
"$defs": {
|
|
19
|
+
"jwk": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"minProperties": 1,
|
|
22
|
+
"additionalProperties": true
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|