@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.
- package/README.md +31 -45
- package/assets/commands/temporal-up.md +1 -1
- package/assets/skills/auth-setup/SKILL.md +1 -1
- package/assets/skills/local-dev/SKILL.md +17 -7
- package/assets/skills/ory-build-agent/SKILL.md +43 -97
- package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -18
- package/assets/skills/ory-temporal-worker/SKILL.md +38 -42
- package/assets/skills/permissions-onboarding/SKILL.md +131 -104
- package/dist/adapters.d.ts +93 -30
- package/dist/adapters.js +464 -136
- package/dist/agent-auth.d.ts +258 -68
- package/dist/agent-auth.js +998 -202
- package/dist/auth-store.d.ts +37 -2
- package/dist/auth-store.js +37 -3
- package/dist/auth.d.ts +40 -4
- package/dist/auth.js +247 -19
- package/dist/bash-parser.d.ts +98 -0
- package/dist/bash-parser.js +396 -0
- package/dist/branding.d.ts +128 -0
- package/dist/branding.js +151 -0
- package/dist/build-info.json +4 -4
- package/dist/cli-invocation.d.ts +1 -1
- package/dist/cli-invocation.js +2 -1
- package/dist/cli.d.ts +20 -29
- package/dist/cli.js +271 -278
- package/dist/client.d.ts +175 -138
- package/dist/client.js +672 -391
- package/dist/config.d.ts +249 -57
- package/dist/config.js +486 -62
- package/dist/context.d.ts +10 -0
- package/dist/context.js +21 -0
- package/dist/contract-suite.d.ts +8 -8
- package/dist/contract-suite.js +88 -69
- package/dist/denial.d.ts +36 -3
- package/dist/denial.js +79 -10
- package/dist/event-reporter.d.ts +77 -0
- package/dist/event-reporter.js +776 -0
- package/dist/external-registrations-main.d.ts +10 -0
- package/dist/external-registrations-main.js +38 -0
- package/dist/external-registrations.d.ts +79 -0
- package/dist/external-registrations.js +188 -0
- package/dist/help-cli.d.ts +39 -0
- package/dist/help-cli.js +55 -0
- package/dist/hook-timeout.d.ts +64 -0
- package/dist/hook-timeout.js +88 -0
- package/dist/index.d.ts +31 -19
- package/dist/index.js +182 -31
- package/dist/lifecycle.d.ts +3 -3
- package/dist/lifecycle.js +38 -6
- package/dist/local/cli.js +11 -6
- package/dist/local/configs.d.ts +74 -18
- package/dist/local/configs.js +291 -84
- package/dist/local/health.d.ts +14 -0
- package/dist/local/health.js +50 -4
- package/dist/local/index.d.ts +2 -2
- package/dist/local/index.js +24 -10
- package/dist/local/manager.d.ts +20 -1
- package/dist/local/manager.js +160 -39
- package/dist/local/ports.d.ts +158 -0
- package/dist/local/ports.js +443 -0
- package/dist/local/seed.d.ts +22 -25
- package/dist/local/seed.js +88 -56
- package/dist/logger.d.ts +54 -25
- package/dist/logger.js +329 -63
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +10 -5
- package/dist/mirror-bootstrap.d.ts +48 -0
- package/dist/mirror-bootstrap.js +254 -0
- package/dist/opl.d.ts +289 -0
- package/dist/opl.js +446 -0
- package/dist/permission-mode.d.ts +87 -0
- package/dist/permission-mode.js +307 -0
- package/dist/permissions-cli.d.ts +13 -49
- package/dist/permissions-cli.js +154 -348
- package/dist/permissions.d.ts +148 -38
- package/dist/permissions.js +591 -45
- package/dist/post-install.d.ts +33 -0
- package/dist/post-install.js +127 -0
- package/dist/read-credential.d.ts +65 -0
- package/dist/read-credential.js +86 -0
- package/dist/registry/cli.js +5 -2
- package/dist/registry/config.d.ts +0 -17
- package/dist/registry/config.js +0 -23
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +2 -2
- package/dist/registry/manager.d.ts +4 -21
- package/dist/registry/manager.js +83 -55
- package/dist/runtime-credential.d.ts +140 -0
- package/dist/runtime-credential.js +572 -0
- package/dist/runtime.d.ts +408 -0
- package/dist/runtime.js +748 -0
- package/dist/setup.d.ts +23 -28
- package/dist/setup.js +57 -84
- package/dist/status-cli.d.ts +29 -13
- package/dist/status-cli.js +124 -144
- package/dist/status-data.d.ts +195 -0
- package/dist/status-data.js +333 -0
- package/dist/status-system.d.ts +24 -0
- package/dist/status-system.js +56 -0
- package/dist/subject.d.ts +126 -20
- package/dist/subject.js +215 -30
- package/dist/testing.d.ts +74 -38
- package/dist/testing.js +185 -68
- package/dist/tool-catalog.d.ts +53 -11
- package/dist/tool-catalog.js +164 -13
- package/dist/tool-metadata.d.ts +7 -6
- package/dist/tool-metadata.js +6 -5
- package/dist/types.d.ts +11 -1
- package/dist/uninstall.d.ts +74 -19
- package/dist/uninstall.js +224 -49
- package/dist/user-login.d.ts +22 -16
- package/dist/user-login.js +67 -96
- package/dist/watch-cli.d.ts +6 -0
- package/dist/watch-cli.js +217 -0
- package/package.json +3 -11
- package/dist/dev.d.ts +0 -103
- package/dist/dev.js +0 -584
- package/dist/interactive-setup.d.ts +0 -165
- package/dist/interactive-setup.js +0 -1546
- package/dist/local/jaeger-main.d.ts +0 -13
- package/dist/local/jaeger-main.js +0 -85
- package/dist/local/jaeger.d.ts +0 -50
- package/dist/local/jaeger.js +0 -162
- package/dist/otel/exporter.d.ts +0 -17
- package/dist/otel/exporter.js +0 -12
- package/dist/otel/index.d.ts +0 -2
- package/dist/otel/index.js +0 -8
- package/dist/otel/otlp.d.ts +0 -103
- package/dist/otel/otlp.js +0 -385
- package/dist/tracer.d.ts +0 -190
- package/dist/tracer.js +0 -481
- package/dist/watch-sandbox.d.ts +0 -9
- 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
|
+
}
|
package/dist/local/seed.d.ts
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Seed the local Ory environment with test data for development.
|
|
3
3
|
*
|
|
4
|
-
* Creates
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
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-
|
|
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
|
-
/**
|
|
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.
|