@ory/argus 0.13.9 → 1.0.0

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 (133) hide show
  1. package/README.md +31 -45
  2. package/assets/commands/temporal-up.md +1 -1
  3. package/assets/skills/auth-setup/SKILL.md +1 -1
  4. package/assets/skills/local-dev/SKILL.md +17 -7
  5. package/assets/skills/ory-build-agent/SKILL.md +43 -97
  6. package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -18
  7. package/assets/skills/ory-temporal-worker/SKILL.md +38 -42
  8. package/assets/skills/permissions-onboarding/SKILL.md +131 -104
  9. package/dist/adapters.d.ts +93 -30
  10. package/dist/adapters.js +464 -136
  11. package/dist/agent-auth.d.ts +258 -68
  12. package/dist/agent-auth.js +998 -202
  13. package/dist/auth-store.d.ts +37 -2
  14. package/dist/auth-store.js +37 -3
  15. package/dist/auth.d.ts +40 -4
  16. package/dist/auth.js +247 -19
  17. package/dist/bash-parser.d.ts +98 -0
  18. package/dist/bash-parser.js +396 -0
  19. package/dist/branding.d.ts +128 -0
  20. package/dist/branding.js +151 -0
  21. package/dist/build-info.json +4 -4
  22. package/dist/cli-invocation.d.ts +1 -1
  23. package/dist/cli-invocation.js +2 -1
  24. package/dist/cli.d.ts +20 -29
  25. package/dist/cli.js +271 -278
  26. package/dist/client.d.ts +175 -138
  27. package/dist/client.js +672 -391
  28. package/dist/config.d.ts +249 -57
  29. package/dist/config.js +486 -62
  30. package/dist/context.d.ts +10 -0
  31. package/dist/context.js +21 -0
  32. package/dist/contract-suite.d.ts +8 -8
  33. package/dist/contract-suite.js +88 -69
  34. package/dist/denial.d.ts +36 -3
  35. package/dist/denial.js +79 -10
  36. package/dist/event-reporter.d.ts +77 -0
  37. package/dist/event-reporter.js +776 -0
  38. package/dist/external-registrations-main.d.ts +10 -0
  39. package/dist/external-registrations-main.js +38 -0
  40. package/dist/external-registrations.d.ts +79 -0
  41. package/dist/external-registrations.js +188 -0
  42. package/dist/help-cli.d.ts +39 -0
  43. package/dist/help-cli.js +55 -0
  44. package/dist/hook-timeout.d.ts +64 -0
  45. package/dist/hook-timeout.js +88 -0
  46. package/dist/index.d.ts +31 -19
  47. package/dist/index.js +182 -31
  48. package/dist/lifecycle.d.ts +3 -3
  49. package/dist/lifecycle.js +38 -6
  50. package/dist/local/cli.js +11 -6
  51. package/dist/local/configs.d.ts +74 -18
  52. package/dist/local/configs.js +291 -84
  53. package/dist/local/health.d.ts +14 -0
  54. package/dist/local/health.js +50 -4
  55. package/dist/local/index.d.ts +2 -2
  56. package/dist/local/index.js +24 -10
  57. package/dist/local/manager.d.ts +20 -1
  58. package/dist/local/manager.js +160 -39
  59. package/dist/local/ports.d.ts +158 -0
  60. package/dist/local/ports.js +443 -0
  61. package/dist/local/seed.d.ts +22 -25
  62. package/dist/local/seed.js +88 -56
  63. package/dist/logger.d.ts +54 -25
  64. package/dist/logger.js +329 -63
  65. package/dist/mcp.d.ts +2 -2
  66. package/dist/mcp.js +10 -5
  67. package/dist/mirror-bootstrap.d.ts +48 -0
  68. package/dist/mirror-bootstrap.js +254 -0
  69. package/dist/opl.d.ts +289 -0
  70. package/dist/opl.js +446 -0
  71. package/dist/permission-mode.d.ts +87 -0
  72. package/dist/permission-mode.js +307 -0
  73. package/dist/permissions-cli.d.ts +13 -49
  74. package/dist/permissions-cli.js +154 -348
  75. package/dist/permissions.d.ts +148 -38
  76. package/dist/permissions.js +591 -45
  77. package/dist/post-install.d.ts +33 -0
  78. package/dist/post-install.js +127 -0
  79. package/dist/read-credential.d.ts +65 -0
  80. package/dist/read-credential.js +86 -0
  81. package/dist/registry/cli.js +5 -2
  82. package/dist/registry/config.d.ts +0 -17
  83. package/dist/registry/config.js +0 -23
  84. package/dist/registry/index.d.ts +1 -1
  85. package/dist/registry/index.js +2 -2
  86. package/dist/registry/manager.d.ts +4 -21
  87. package/dist/registry/manager.js +83 -55
  88. package/dist/runtime-credential.d.ts +140 -0
  89. package/dist/runtime-credential.js +572 -0
  90. package/dist/runtime.d.ts +408 -0
  91. package/dist/runtime.js +748 -0
  92. package/dist/setup.d.ts +23 -28
  93. package/dist/setup.js +57 -84
  94. package/dist/status-cli.d.ts +29 -13
  95. package/dist/status-cli.js +124 -144
  96. package/dist/status-data.d.ts +195 -0
  97. package/dist/status-data.js +333 -0
  98. package/dist/status-system.d.ts +24 -0
  99. package/dist/status-system.js +56 -0
  100. package/dist/subject.d.ts +126 -20
  101. package/dist/subject.js +215 -30
  102. package/dist/testing.d.ts +74 -38
  103. package/dist/testing.js +185 -68
  104. package/dist/tool-catalog.d.ts +53 -11
  105. package/dist/tool-catalog.js +164 -13
  106. package/dist/tool-metadata.d.ts +7 -6
  107. package/dist/tool-metadata.js +6 -5
  108. package/dist/types.d.ts +11 -1
  109. package/dist/uninstall.d.ts +74 -19
  110. package/dist/uninstall.js +224 -49
  111. package/dist/user-login.d.ts +22 -16
  112. package/dist/user-login.js +67 -96
  113. package/dist/watch-cli.d.ts +6 -0
  114. package/dist/watch-cli.js +217 -0
  115. package/package.json +3 -11
  116. package/dist/dev.d.ts +0 -103
  117. package/dist/dev.js +0 -584
  118. package/dist/interactive-setup.d.ts +0 -165
  119. package/dist/interactive-setup.js +0 -1546
  120. package/dist/local/jaeger-main.d.ts +0 -13
  121. package/dist/local/jaeger-main.js +0 -85
  122. package/dist/local/jaeger.d.ts +0 -50
  123. package/dist/local/jaeger.js +0 -162
  124. package/dist/otel/exporter.d.ts +0 -17
  125. package/dist/otel/exporter.js +0 -12
  126. package/dist/otel/index.d.ts +0 -2
  127. package/dist/otel/index.js +0 -8
  128. package/dist/otel/otlp.d.ts +0 -103
  129. package/dist/otel/otlp.js +0 -385
  130. package/dist/tracer.d.ts +0 -190
  131. package/dist/tracer.js +0 -481
  132. package/dist/watch-sandbox.d.ts +0 -9
  133. package/dist/watch-sandbox.js +0 -81
@@ -0,0 +1,443 @@
1
+ "use strict";
2
+ /**
3
+ * Host-facing addressing for the local Ory stack — which ports it publishes on
4
+ * the host, and which host name everything else should use to reach it.
5
+ *
6
+ * Two kinds of port live in this stack, and conflating them is the bug this
7
+ * module exists to prevent:
8
+ *
9
+ * - **Container-internal ports** are what each service listens on *inside* the
10
+ * compose network, and what one service uses to address another
11
+ * (`http://kratos:4433`). They live on a private bridge network, so they can
12
+ * never collide with anything on the host and there is no reason to make them
13
+ * configurable. They are fixed constants below.
14
+ * - **Host ports** are the left-hand side of a compose `ports:` mapping — the
15
+ * only ports that are actually claimed on the developer's machine, and so the
16
+ * only ones that can collide. Every one of them is overridable.
17
+ *
18
+ * The defaults are the stock upstream Ory ports (4433/4434, 4444/4445,
19
+ * 4466/4467, …), which is deliberate — they are the ports every Ory doc and
20
+ * tutorial uses. It also means a developer who already runs Ory locally, which
21
+ * is precisely this stack's audience, collides on first run. Before #63 there
22
+ * was no override and no diagnosis: `docker compose up` failed with Docker's
23
+ * raw "port is already allocated" and the only apparent fix was to hand-edit
24
+ * the generated compose file, which the next `local up` regenerates away.
25
+ *
26
+ * Three levels of control, cheapest first:
27
+ *
28
+ * 1. `ORY_LOCAL_PORT_OFFSET=100` shifts *every* default by a constant, which is
29
+ * the common case ("something else owns the Ory ports, move the whole
30
+ * stack"). Relative spacing is preserved, so the set stays collision-free
31
+ * with itself.
32
+ * 2. `ORY_LOCAL_<SERVICE>_PORT` pins one service exactly, and wins over the
33
+ * offset.
34
+ * 3. `ORY_LOCAL_HOST` changes the host every generated URL advertises, for a
35
+ * remote Docker daemon or an agent running inside a container. It defaults
36
+ * to the host parsed out of `DOCKER_HOST` when that points somewhere other
37
+ * than this machine, so the remote-daemon case usually needs no flag at all.
38
+ */
39
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
40
+ if (k2 === undefined) k2 = k;
41
+ var desc = Object.getOwnPropertyDescriptor(m, k);
42
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
43
+ desc = { enumerable: true, get: function() { return m[k]; } };
44
+ }
45
+ Object.defineProperty(o, k2, desc);
46
+ }) : (function(o, m, k, k2) {
47
+ if (k2 === undefined) k2 = k;
48
+ o[k2] = m[k];
49
+ }));
50
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
51
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
52
+ }) : function(o, v) {
53
+ o["default"] = v;
54
+ });
55
+ var __importStar = (this && this.__importStar) || (function () {
56
+ var ownKeys = function(o) {
57
+ ownKeys = Object.getOwnPropertyNames || function (o) {
58
+ var ar = [];
59
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
60
+ return ar;
61
+ };
62
+ return ownKeys(o);
63
+ };
64
+ return function (mod) {
65
+ if (mod && mod.__esModule) return mod;
66
+ var result = {};
67
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
68
+ __setModuleDefault(result, mod);
69
+ return result;
70
+ };
71
+ })();
72
+ Object.defineProperty(exports, "__esModule", { value: true });
73
+ exports.DEFAULT_LOCAL_HOST = exports.SKIP_PORT_CHECK_ENV_VAR = exports.LOCAL_HOST_ENV_VAR = exports.PORT_OFFSET_ENV_VAR = exports.LOCAL_PORT_SPECS = exports.LOCAL_PORT_NAMES = exports.CONSOLE_CONTAINER_PORT = exports.OATHKEEPER_API_CONTAINER_PORT = exports.OATHKEEPER_PROXY_CONTAINER_PORT = exports.LOGIN_UI_CONTAINER_PORT = exports.GATEWAY_CONTAINER_PORT = exports.HYDRA_TOKEN_USER_CONTAINER_PORT = exports.HYDRA_ADMIN_CONTAINER_PORT = exports.HYDRA_PUBLIC_CONTAINER_PORT = exports.KETO_WRITE_CONTAINER_PORT = exports.KETO_READ_CONTAINER_PORT = exports.KRATOS_ADMIN_CONTAINER_PORT = exports.KRATOS_PUBLIC_CONTAINER_PORT = void 0;
74
+ exports.portCheckDisabled = portCheckDisabled;
75
+ exports.resolveLocalPorts = resolveLocalPorts;
76
+ exports.hostFromDockerHost = hostFromDockerHost;
77
+ exports.isLoopbackHost = isLoopbackHost;
78
+ exports.resolveLocalHost = resolveLocalHost;
79
+ exports.isLocalDockerDaemon = isLocalDockerDaemon;
80
+ exports.hostUrl = hostUrl;
81
+ exports.isPortAvailable = isPortAvailable;
82
+ exports.findPortConflicts = findPortConflicts;
83
+ exports.describePortConflicts = describePortConflicts;
84
+ const net = __importStar(require("node:net"));
85
+ // ─── Container-internal ports (fixed) ──────────────────────────────
86
+ //
87
+ // Changing any of these would also mean rewriting the service's own listen
88
+ // config and every cross-service URL on the compose network, for no benefit:
89
+ // nothing outside the bridge network can see them.
90
+ exports.KRATOS_PUBLIC_CONTAINER_PORT = 4433;
91
+ exports.KRATOS_ADMIN_CONTAINER_PORT = 4434;
92
+ exports.KETO_READ_CONTAINER_PORT = 4466;
93
+ exports.KETO_WRITE_CONTAINER_PORT = 4467;
94
+ exports.HYDRA_PUBLIC_CONTAINER_PORT = 4444;
95
+ exports.HYDRA_ADMIN_CONTAINER_PORT = 4445;
96
+ exports.HYDRA_TOKEN_USER_CONTAINER_PORT = 5555;
97
+ exports.GATEWAY_CONTAINER_PORT = 4000;
98
+ exports.LOGIN_UI_CONTAINER_PORT = 4455;
99
+ exports.OATHKEEPER_PROXY_CONTAINER_PORT = 4456;
100
+ exports.OATHKEEPER_API_CONTAINER_PORT = 4457;
101
+ /** Console Lite's Dockerfile sets `ENV PORT=3000`. */
102
+ exports.CONSOLE_CONTAINER_PORT = 3000;
103
+ // ─── Host ports (overridable) ──────────────────────────────────────
104
+ /** Every host port the stack publishes, in the order `local status` lists them. */
105
+ exports.LOCAL_PORT_NAMES = [
106
+ "gateway",
107
+ "console",
108
+ "loginUi",
109
+ "kratosPublic",
110
+ "kratosAdmin",
111
+ "ketoRead",
112
+ "ketoWrite",
113
+ "hydraPublic",
114
+ "hydraAdmin",
115
+ "hydraTokenUser",
116
+ "oathkeeperProxy",
117
+ "oathkeeperApi",
118
+ ];
119
+ /**
120
+ * The port table. `default` doubles as the container-internal port for every
121
+ * service, which is what keeps the generated mapping readable
122
+ * (`"4433:4433"` by default, `"5433:4433"` once overridden).
123
+ */
124
+ exports.LOCAL_PORT_SPECS = {
125
+ gateway: {
126
+ default: exports.GATEWAY_CONTAINER_PORT,
127
+ envVar: "ORY_LOCAL_GATEWAY_PORT",
128
+ label: "API gateway",
129
+ },
130
+ console: {
131
+ default: 4100,
132
+ envVar: "ORY_LOCAL_CONSOLE_PORT",
133
+ label: "Ory Console",
134
+ },
135
+ loginUi: {
136
+ default: exports.LOGIN_UI_CONTAINER_PORT,
137
+ envVar: "ORY_LOCAL_LOGIN_UI_PORT",
138
+ label: "Login UI",
139
+ },
140
+ kratosPublic: {
141
+ default: exports.KRATOS_PUBLIC_CONTAINER_PORT,
142
+ envVar: "ORY_LOCAL_KRATOS_PUBLIC_PORT",
143
+ label: "Kratos (public)",
144
+ },
145
+ kratosAdmin: {
146
+ default: exports.KRATOS_ADMIN_CONTAINER_PORT,
147
+ envVar: "ORY_LOCAL_KRATOS_ADMIN_PORT",
148
+ label: "Kratos (admin)",
149
+ },
150
+ ketoRead: {
151
+ default: exports.KETO_READ_CONTAINER_PORT,
152
+ envVar: "ORY_LOCAL_KETO_READ_PORT",
153
+ label: "Keto (read)",
154
+ },
155
+ ketoWrite: {
156
+ default: exports.KETO_WRITE_CONTAINER_PORT,
157
+ envVar: "ORY_LOCAL_KETO_WRITE_PORT",
158
+ label: "Keto (write)",
159
+ },
160
+ hydraPublic: {
161
+ default: exports.HYDRA_PUBLIC_CONTAINER_PORT,
162
+ envVar: "ORY_LOCAL_HYDRA_PUBLIC_PORT",
163
+ label: "Hydra (public)",
164
+ },
165
+ hydraAdmin: {
166
+ default: exports.HYDRA_ADMIN_CONTAINER_PORT,
167
+ envVar: "ORY_LOCAL_HYDRA_ADMIN_PORT",
168
+ label: "Hydra (admin)",
169
+ },
170
+ hydraTokenUser: {
171
+ default: exports.HYDRA_TOKEN_USER_CONTAINER_PORT,
172
+ envVar: "ORY_LOCAL_HYDRA_TOKEN_USER_PORT",
173
+ label: "Hydra (token user)",
174
+ },
175
+ oathkeeperProxy: {
176
+ default: exports.OATHKEEPER_PROXY_CONTAINER_PORT,
177
+ envVar: "ORY_LOCAL_OATHKEEPER_PROXY_PORT",
178
+ label: "Oathkeeper (proxy)",
179
+ },
180
+ oathkeeperApi: {
181
+ default: exports.OATHKEEPER_API_CONTAINER_PORT,
182
+ envVar: "ORY_LOCAL_OATHKEEPER_API_PORT",
183
+ label: "Oathkeeper (API)",
184
+ },
185
+ };
186
+ /** Shifts every default port by a constant. Overridden per-port by the specific var. */
187
+ exports.PORT_OFFSET_ENV_VAR = "ORY_LOCAL_PORT_OFFSET";
188
+ /** Overrides the host name every generated URL advertises. */
189
+ exports.LOCAL_HOST_ENV_VAR = "ORY_LOCAL_HOST";
190
+ /**
191
+ * Skips every port probe — the pre-start preflight *and* the post-failure
192
+ * diagnosis.
193
+ *
194
+ * Both open real sockets, which is the right thing before starting containers
195
+ * and the wrong thing in a unit test (the core suite sets this so no test
196
+ * depends on which ports happen to be free on the machine running it) or in a
197
+ * sandbox that forbids listening. The sandbox case is why this covers the
198
+ * diagnosis too: where binding is denied, `isPortAvailable` answers "taken" for
199
+ * every port, so an unrelated startup failure would otherwise be reported as a
200
+ * confident and completely wrong "every port is in use".
201
+ */
202
+ exports.SKIP_PORT_CHECK_ENV_VAR = "ORY_LOCAL_SKIP_PORT_CHECK";
203
+ function portCheckDisabled(env = process.env) {
204
+ const raw = env[exports.SKIP_PORT_CHECK_ENV_VAR]?.trim().toLowerCase();
205
+ if (!raw)
206
+ return false;
207
+ return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
208
+ }
209
+ const MIN_PORT = 1;
210
+ const MAX_PORT = 65535;
211
+ function parsePort(raw) {
212
+ // Reject anything that isn't a bare integer — `"4000x"`, `"40.5"`, `" "`.
213
+ // parseInt would happily accept the first two and produce a port the user
214
+ // did not ask for, which is worse than ignoring the value.
215
+ if (!/^\d+$/.test(raw))
216
+ return undefined;
217
+ const value = Number(raw);
218
+ if (!Number.isInteger(value) || value < MIN_PORT || value > MAX_PORT) {
219
+ return undefined;
220
+ }
221
+ return value;
222
+ }
223
+ /**
224
+ * Resolve every host port from the environment.
225
+ *
226
+ * Invalid values are **ignored with a warning** rather than throwing: this runs
227
+ * on the way into `local up`, and refusing to start because one env var has a
228
+ * typo would be a worse outcome than starting on the documented default and
229
+ * saying so. Warnings are returned rather than printed so the caller controls
230
+ * the output stream.
231
+ */
232
+ function resolveLocalPorts(env = process.env) {
233
+ const warnings = [];
234
+ let offset = 0;
235
+ const rawOffset = env[exports.PORT_OFFSET_ENV_VAR]?.trim();
236
+ if (rawOffset) {
237
+ const parsed = /^-?\d+$/.test(rawOffset) ? Number(rawOffset) : undefined;
238
+ if (parsed === undefined) {
239
+ warnings.push(`${exports.PORT_OFFSET_ENV_VAR}="${rawOffset}" is not an integer — ignoring it and using the default ports.`);
240
+ }
241
+ else {
242
+ offset = parsed;
243
+ }
244
+ }
245
+ const ports = {};
246
+ for (const name of exports.LOCAL_PORT_NAMES) {
247
+ const spec = exports.LOCAL_PORT_SPECS[name];
248
+ const raw = env[spec.envVar]?.trim();
249
+ if (raw) {
250
+ const parsed = parsePort(raw);
251
+ if (parsed === undefined) {
252
+ warnings.push(`${spec.envVar}="${raw}" is not a port between ${MIN_PORT} and ${MAX_PORT} — using ${spec.default} for ${spec.label}.`);
253
+ ports[name] = spec.default;
254
+ }
255
+ else {
256
+ ports[name] = parsed;
257
+ }
258
+ continue;
259
+ }
260
+ if (offset !== 0) {
261
+ const shifted = spec.default + offset;
262
+ if (shifted < MIN_PORT || shifted > MAX_PORT) {
263
+ warnings.push(`${exports.PORT_OFFSET_ENV_VAR}=${offset} puts ${spec.label} at ${shifted}, outside ${MIN_PORT}-${MAX_PORT} — using ${spec.default}. Set ${spec.envVar} to place it explicitly.`);
264
+ ports[name] = spec.default;
265
+ }
266
+ else {
267
+ ports[name] = shifted;
268
+ }
269
+ continue;
270
+ }
271
+ ports[name] = spec.default;
272
+ }
273
+ warnings.push(...describeDuplicatePorts(ports));
274
+ return { ports, warnings };
275
+ }
276
+ /**
277
+ * Two services configured onto the same host port. Only reachable through
278
+ * explicit per-port overrides — the offset preserves the spacing of the
279
+ * defaults — and it would otherwise surface as a confusing Docker error about
280
+ * a port the user *did* mean to claim.
281
+ */
282
+ function describeDuplicatePorts(ports) {
283
+ const byPort = new Map();
284
+ for (const name of exports.LOCAL_PORT_NAMES) {
285
+ const list = byPort.get(ports[name]) ?? [];
286
+ list.push(name);
287
+ byPort.set(ports[name], list);
288
+ }
289
+ const warnings = [];
290
+ for (const [port, names] of byPort) {
291
+ if (names.length < 2)
292
+ continue;
293
+ const labels = names.map((n) => exports.LOCAL_PORT_SPECS[n].label).join(" and ");
294
+ const vars = names.map((n) => exports.LOCAL_PORT_SPECS[n].envVar).join(" / ");
295
+ warnings.push(`${labels} are both configured on host port ${port} — the stack cannot start. Set ${vars} to different values.`);
296
+ }
297
+ return warnings;
298
+ }
299
+ exports.DEFAULT_LOCAL_HOST = "localhost";
300
+ /**
301
+ * Pull the host out of a `DOCKER_HOST` value.
302
+ *
303
+ * `unix://` and `npipe://` sockets are this machine, so they yield nothing and
304
+ * the default stands. A `tcp://` or `ssh://` daemon publishes the stack's ports
305
+ * on *its* host, not ours, so that host is what generated URLs have to
306
+ * advertise — otherwise every URL we print and persist points at a machine that
307
+ * isn't running the stack.
308
+ */
309
+ function hostFromDockerHost(dockerHost) {
310
+ const raw = dockerHost?.trim();
311
+ if (!raw)
312
+ return undefined;
313
+ if (raw.startsWith("unix://") || raw.startsWith("npipe://"))
314
+ return undefined;
315
+ let hostname;
316
+ try {
317
+ hostname = new URL(raw).hostname;
318
+ }
319
+ catch {
320
+ return undefined;
321
+ }
322
+ if (!hostname)
323
+ return undefined;
324
+ // A tcp:// daemon on loopback is still this machine — `localhost` reads
325
+ // better in a banner than `127.0.0.1`, and keeps URLs stable for anyone who
326
+ // sets DOCKER_HOST explicitly at the default address.
327
+ if (isLoopbackHost(hostname))
328
+ return undefined;
329
+ return hostname;
330
+ }
331
+ function isLoopbackHost(host) {
332
+ const h = host.toLowerCase().replace(/^\[|\]$/g, "");
333
+ return h === "localhost" || h === "127.0.0.1" || h === "::1";
334
+ }
335
+ /**
336
+ * The host name every generated URL should advertise: config files, banners,
337
+ * the health probe, and the `projectUrl` that `local configure` persists.
338
+ */
339
+ function resolveLocalHost(env = process.env) {
340
+ const explicit = env[exports.LOCAL_HOST_ENV_VAR]?.trim();
341
+ if (explicit)
342
+ return { host: explicit, source: "env" };
343
+ const fromDocker = hostFromDockerHost(env.DOCKER_HOST);
344
+ if (fromDocker)
345
+ return { host: fromDocker, source: "docker-host" };
346
+ return { host: exports.DEFAULT_LOCAL_HOST, source: "default" };
347
+ }
348
+ /**
349
+ * True when the Docker daemon publishes ports on *this* machine, which is the
350
+ * only case where probing a local socket says anything about whether the stack
351
+ * will be able to bind. With a remote daemon the probe would test the wrong
352
+ * host, so callers skip the preflight entirely rather than report a conflict
353
+ * that isn't there (or miss one that is).
354
+ */
355
+ function isLocalDockerDaemon(env = process.env) {
356
+ return hostFromDockerHost(env.DOCKER_HOST) === undefined;
357
+ }
358
+ /** Build an `http://host:port` origin, bracketing bare IPv6 literals. */
359
+ function hostUrl(host, port) {
360
+ const needsBrackets = host.includes(":") && !host.startsWith("[");
361
+ return `http://${needsBrackets ? `[${host}]` : host}:${port}`;
362
+ }
363
+ /**
364
+ * Can we bind this port? Docker publishes on all interfaces, so we test the
365
+ * same thing it will do. A port held on loopback alone still fails a
366
+ * wildcard bind, so this catches both.
367
+ *
368
+ * Anything other than a clean listen counts as unavailable: `EADDRINUSE` is the
369
+ * case we are looking for, and `EACCES` (a privileged port without rights)
370
+ * would fail the container just the same.
371
+ */
372
+ function isPortAvailable(port, timeoutMs = 500) {
373
+ return new Promise((resolve) => {
374
+ const server = net.createServer();
375
+ let settled = false;
376
+ const finish = (available) => {
377
+ if (settled)
378
+ return;
379
+ settled = true;
380
+ clearTimeout(timer);
381
+ server.removeAllListeners();
382
+ try {
383
+ server.close();
384
+ }
385
+ catch {
386
+ /* already closed */
387
+ }
388
+ resolve(available);
389
+ };
390
+ const timer = setTimeout(() => finish(true), timeoutMs);
391
+ timer.unref?.();
392
+ server.once("error", () => finish(false));
393
+ server.once("listening", () => finish(true));
394
+ try {
395
+ server.listen({ port, host: "0.0.0.0", exclusive: true });
396
+ }
397
+ catch {
398
+ finish(false);
399
+ }
400
+ });
401
+ }
402
+ /**
403
+ * Which of the stack's host ports are already taken. Probed concurrently —
404
+ * twelve sequential binds would add noticeable latency to every `local up`.
405
+ */
406
+ async function findPortConflicts(ports) {
407
+ const checks = exports.LOCAL_PORT_NAMES.map(async (name) => {
408
+ const available = await isPortAvailable(ports[name]);
409
+ return available
410
+ ? undefined
411
+ : {
412
+ name,
413
+ port: ports[name],
414
+ label: exports.LOCAL_PORT_SPECS[name].label,
415
+ envVar: exports.LOCAL_PORT_SPECS[name].envVar,
416
+ };
417
+ });
418
+ return (await Promise.all(checks)).filter((c) => c !== undefined);
419
+ }
420
+ /**
421
+ * The message a user gets instead of Docker's "port is already allocated":
422
+ * which service wanted which port, and the two ways to move it.
423
+ */
424
+ function describePortConflicts(conflicts) {
425
+ if (conflicts.length === 0)
426
+ return "";
427
+ const lines = [
428
+ conflicts.length === 1
429
+ ? "Port already in use — the local Ory stack cannot start:"
430
+ : `${conflicts.length} ports already in use — the local Ory stack cannot start:`,
431
+ "",
432
+ ...conflicts.map((c) => ` ${c.port} ${c.label} (override with ${c.envVar})`),
433
+ "",
434
+ "The defaults are the stock Ory ports, so another Ory instance is the usual cause.",
435
+ "Move the whole stack out of the way with a single offset:",
436
+ "",
437
+ ` ${exports.PORT_OFFSET_ENV_VAR}=100 <command>`,
438
+ "",
439
+ "or pin individual services with the variables listed above. Do not edit the",
440
+ "generated compose file — the next 'local up' regenerates it.",
441
+ ];
442
+ return lines.join("\n");
443
+ }
@@ -1,18 +1,16 @@
1
1
  /**
2
2
  * Seed the local Ory environment with test data for development.
3
3
  *
4
- * Creates two principals:
4
+ * Creates the **User** (`user@ory-local.dev`) — a human identity with a
5
+ * known password (printed by the launcher) and an OAuth2
6
+ * `authorization_code` + PKCE client registered against the loopback
7
+ * redirect URIs. The launcher always runs the real PKCE browser flow; the
8
+ * user types these credentials into the login UI to complete the flow.
5
9
  *
6
- * 1. **Agent** (`agent@ory-local.dev`) a Kratos identity that the
7
- * harness will register a DCR OAuth2 client *for* on first session
8
- * start. No OAuth2 client is pre-registered here.
9
- *
10
- * 2. **User** (`user@ory-local.dev`) — a human identity with a known
11
- * password (printed by the launcher) and an OAuth2
12
- * `authorization_code` + PKCE client registered against the
13
- * loopback redirect URIs. The launcher always runs the real PKCE
14
- * browser flow; the user types these credentials into the login
15
- * UI to complete the flow.
10
+ * No agent principal is seeded: the harness self-registers a DCR OAuth2
11
+ * client for the agent on first session start (using the user's bearer as
12
+ * the initial access token), and permission checks are made against the
13
+ * user subject — so a pre-seeded agent Kratos identity would be unused.
16
14
  *
17
15
  * Permission tuples are written as SubjectSet `User:<identity.id>` so
18
16
  * the Console's *Add relationship* dialog (which always emits SubjectSet
@@ -24,12 +22,16 @@
24
22
  * recreates OAuth2 clients (so the secret is always known).
25
23
  */
26
24
  /**
27
- * Stable client id Hydra issues to the seeded user PKCE client. Stable
28
- * so `local configure` can persist it to the shared config and downstream
29
- * runs can rely on `ORY_OAUTH2_CLIENT_ID` resolution without first
25
+ * The client id Hydra issues to the seeded user PKCE client: the same
26
+ * **reserved** id Ory Network's Agent Security setup uses, so local dev and a
27
+ * hosted project name the shared login client identically and the id — never a
28
+ * `client_name` — is what resolves it in both.
29
+ *
30
+ * Stable, so `local configure` can persist it to the shared config and
31
+ * downstream runs can rely on `ORY_OAUTH2_CLIENT_ID` resolution without first
30
32
  * dragging the value out of the `local up` banner.
31
33
  */
32
- export declare const USER_CLIENT_ID = "ory-user-local";
34
+ export declare const USER_CLIENT_ID = "ory-agent-security-login";
33
35
  export interface SeededIdentity {
34
36
  id: string;
35
37
  email: string;
@@ -39,15 +41,6 @@ export interface SeededOAuth2Client {
39
41
  clientSecret?: string;
40
42
  }
41
43
  export interface SeedResult {
42
- /**
43
- * Agent (machine) identity — Kratos identity only. The agent's OAuth2
44
- * client is *not* pre-registered: the harness self-registers via DCR
45
- * (RFC 7591) on first session start using the user's bearer as the
46
- * initial access token.
47
- */
48
- agent: {
49
- identity: SeededIdentity;
50
- };
51
44
  /** User (human) identity — subject of permission checks. */
52
45
  user: {
53
46
  identity: SeededIdentity;
@@ -67,7 +60,11 @@ export interface SeedResult {
67
60
  tuples: number;
68
61
  };
69
62
  }
70
- /** Subject namespace for user identities in seeded permission tuples. */
63
+ /**
64
+ * Subject namespace for user identities in seeded permission tuples. Re-exported
65
+ * from the core default so the seed, the runtime resolver, and the persisted
66
+ * install config all address the user under the same namespace.
67
+ */
71
68
  export declare const USER_SUBJECT_NAMESPACE = "User";
72
69
  /**
73
70
  * Run the full seed process. Idempotent — safe to run multiple times.