@the-40-thieves/obsidian-tc-shared 1.31.7 → 1.32.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/dist/config/auth-acl.schema.d.ts +37 -0
- package/dist/config/auth-acl.schema.d.ts.map +1 -1
- package/dist/config/auth-acl.schema.js +184 -18
- package/dist/config/auth-acl.schema.js.map +1 -1
- package/dist/config/network.schema.d.ts +5 -0
- package/dist/config/network.schema.d.ts.map +1 -0
- package/dist/config/network.schema.js +25 -0
- package/dist/config/network.schema.js.map +1 -0
- package/dist/config/observability.schema.d.ts +36 -0
- package/dist/config/observability.schema.d.ts.map +1 -1
- package/dist/config/observability.schema.js +141 -10
- package/dist/config/observability.schema.js.map +1 -1
- package/dist/config/path-glob.d.ts +17 -0
- package/dist/config/path-glob.d.ts.map +1 -0
- package/dist/config/path-glob.js +118 -0
- package/dist/config/path-glob.js.map +1 -0
- package/dist/config/provenance.schema.d.ts +21 -0
- package/dist/config/provenance.schema.d.ts.map +1 -0
- package/dist/config/provenance.schema.js +72 -0
- package/dist/config/provenance.schema.js.map +1 -0
- package/dist/config/reranker.schema.d.ts +4 -0
- package/dist/config/reranker.schema.d.ts.map +1 -1
- package/dist/config/reranker.schema.js +7 -0
- package/dist/config/reranker.schema.js.map +1 -1
- package/dist/config/retrieval.schema.d.ts +10 -2
- package/dist/config/retrieval.schema.d.ts.map +1 -1
- package/dist/config/retrieval.schema.js +53 -60
- package/dist/config/retrieval.schema.js.map +1 -1
- package/dist/config/runtime.schema.d.ts +15 -0
- package/dist/config/runtime.schema.d.ts.map +1 -1
- package/dist/config/runtime.schema.js +37 -0
- package/dist/config/runtime.schema.js.map +1 -1
- package/dist/config/server.schema.d.ts +328 -4
- package/dist/config/server.schema.d.ts.map +1 -1
- package/dist/config/server.schema.js +70 -4
- package/dist/config/server.schema.js.map +1 -1
- package/dist/config/tools.schema.d.ts +21 -0
- package/dist/config/tools.schema.d.ts.map +1 -1
- package/dist/config/tools.schema.js +38 -12
- package/dist/config/tools.schema.js.map +1 -1
- package/dist/config/vault.schema.d.ts +41 -0
- package/dist/config/vault.schema.d.ts.map +1 -1
- package/dist/config/vault.schema.js +57 -2
- package/dist/config/vault.schema.js.map +1 -1
- package/dist/config/wiki-judge.schema.d.ts +20 -0
- package/dist/config/wiki-judge.schema.d.ts.map +1 -0
- package/dist/config/wiki-judge.schema.js +88 -0
- package/dist/config/wiki-judge.schema.js.map +1 -0
- package/dist/config.schema.d.ts +6 -5
- package/dist/config.schema.d.ts.map +1 -1
- package/dist/config.schema.js +4 -3
- package/dist/config.schema.js.map +1 -1
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -2
- package/dist/errors.js.map +1 -1
- package/dist/net-host.d.ts +44 -0
- package/dist/net-host.d.ts.map +1 -1
- package/dist/net-host.js +249 -0
- package/dist/net-host.js.map +1 -1
- package/dist/schemas/primitives.d.ts +31 -0
- package/dist/schemas/primitives.d.ts.map +1 -1
- package/dist/schemas/primitives.js +54 -0
- package/dist/schemas/primitives.js.map +1 -1
- package/dist/scopes.d.ts +1 -0
- package/dist/scopes.d.ts.map +1 -1
- package/dist/scopes.js +10 -0
- package/dist/scopes.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,9 +3,46 @@ export declare const AuthConfigSchema: z.ZodObject<{
|
|
|
3
3
|
mode: z.ZodDefault<z.ZodEnum<{
|
|
4
4
|
jwt: "jwt";
|
|
5
5
|
none: "none";
|
|
6
|
+
oidc: "oidc";
|
|
6
7
|
}>>;
|
|
8
|
+
oidc: z.ZodOptional<z.ZodObject<{
|
|
9
|
+
issuer: z.ZodString;
|
|
10
|
+
audience: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
|
|
11
|
+
clientId: z.ZodOptional<z.ZodString>;
|
|
12
|
+
jwksUri: z.ZodOptional<z.ZodString>;
|
|
13
|
+
allowedJwksHosts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
14
|
+
allowPrivateNetwork: z.ZodDefault<z.ZodBoolean>;
|
|
15
|
+
allowedAlgs: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
16
|
+
ES256: "ES256";
|
|
17
|
+
ES384: "ES384";
|
|
18
|
+
ES512: "ES512";
|
|
19
|
+
EdDSA: "EdDSA";
|
|
20
|
+
PS256: "PS256";
|
|
21
|
+
PS384: "PS384";
|
|
22
|
+
PS512: "PS512";
|
|
23
|
+
RS256: "RS256";
|
|
24
|
+
RS384: "RS384";
|
|
25
|
+
RS512: "RS512";
|
|
26
|
+
}>>>;
|
|
27
|
+
clockToleranceSeconds: z.ZodDefault<z.ZodNumber>;
|
|
28
|
+
discoveryCacheSeconds: z.ZodDefault<z.ZodNumber>;
|
|
29
|
+
requireAtJwtType: z.ZodDefault<z.ZodBoolean>;
|
|
30
|
+
claimMapping: z.ZodDefault<z.ZodObject<{
|
|
31
|
+
subject: z.ZodDefault<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
32
|
+
scopes: z.ZodDefault<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
33
|
+
scopeMap: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
34
|
+
principal: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
35
|
+
vault: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
36
|
+
allowedVaults: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
37
|
+
persona: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
38
|
+
allowedPersonas: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
39
|
+
}, z.core.$strict>>;
|
|
40
|
+
requiredClaims: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>]>>;
|
|
41
|
+
}, z.core.$strict>>;
|
|
7
42
|
jwtSecret: z.ZodOptional<z.ZodString>;
|
|
8
43
|
tokenTtlSeconds: z.ZodDefault<z.ZodNumber>;
|
|
44
|
+
rotationGraceSeconds: z.ZodDefault<z.ZodNumber>;
|
|
45
|
+
requireJti: z.ZodDefault<z.ZodBoolean>;
|
|
9
46
|
jwks: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
10
47
|
jwksFile: z.ZodOptional<z.ZodString>;
|
|
11
48
|
jwksUri: z.ZodOptional<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-acl.schema.d.ts","sourceRoot":"","sources":["../../src/config/auth-acl.schema.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-acl.schema.d.ts","sourceRoot":"","sources":["../../src/config/auth-acl.schema.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAyNxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsI3B,CAAC;AACH,eAAO,MAAM,aAAa;;;iBAYxB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;;;iBAiD1B,CAAC"}
|
|
@@ -6,23 +6,191 @@
|
|
|
6
6
|
// move here with their schema; a refinement that reads ANOTHER domain (e.g. the http/auth
|
|
7
7
|
// interlock in ServerConfigSchema.superRefine) stays in config.schema.ts.
|
|
8
8
|
import { z } from "zod";
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
import { isQualifiedScope } from "../scopes";
|
|
10
|
+
import { aclPathGlob } from "./path-glob";
|
|
11
|
+
// `auth.oidc`: verify access tokens issued by an EXTERNAL OpenID Connect provider (bring your own
|
|
12
|
+
// IdP). Verification only — obsidian-tc stays a resource server; the bundled authorization server
|
|
13
|
+
// is a separate design and lives under `auth.as`, a namespace this block does not use.
|
|
14
|
+
//
|
|
15
|
+
// Only asymmetric algorithms can be allowed. HS* never verifies an IdP token (the "key" would be a
|
|
16
|
+
// public value) and `none` is no signature at all, so neither is expressible, not merely defaulted off.
|
|
17
|
+
const OIDC_ALLOWED_ALGS = [
|
|
18
|
+
"RS256",
|
|
19
|
+
"RS384",
|
|
20
|
+
"RS512",
|
|
21
|
+
"PS256",
|
|
22
|
+
"PS384",
|
|
23
|
+
"PS512",
|
|
24
|
+
"ES256",
|
|
25
|
+
"ES384",
|
|
26
|
+
"ES512",
|
|
27
|
+
"EdDSA",
|
|
28
|
+
];
|
|
29
|
+
const isHttpsUrl = (v, o) => {
|
|
30
|
+
try {
|
|
31
|
+
const u = new URL(v);
|
|
32
|
+
return (u.protocol === "https:" &&
|
|
33
|
+
u.username === "" &&
|
|
34
|
+
u.password === "" &&
|
|
35
|
+
(o.allowQueryFragment || (u.search === "" && u.hash === "")));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
// A claim location: a dotted path into NESTED objects (`realm_access.roles`), or an array of literal
|
|
42
|
+
// segments. A top-level claim whose own NAME contains dots (`https://app.example.com/roles`, the
|
|
43
|
+
// Auth0 namespaced form) can only be written as the array `["https://app.example.com/roles"]`: a
|
|
44
|
+
// string is always walked, so a forged top-level `realm_access.roles` never shadows the nested claim.
|
|
45
|
+
const ClaimPathSchema = z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]);
|
|
46
|
+
const HOSTNAME_RE = /^(?=.{1,253}$)[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/;
|
|
47
|
+
const OidcConfigSchema = z
|
|
48
|
+
.strictObject({
|
|
49
|
+
issuer: z
|
|
50
|
+
.string()
|
|
51
|
+
.refine((v) => isHttpsUrl(v, { allowQueryFragment: false }), {
|
|
52
|
+
message: "must be an https URL with no query, fragment or credentials",
|
|
53
|
+
})
|
|
54
|
+
.describe("The identity provider's issuer identifier, an https URL. Compared EXACTLY (no normalisation, a trailing slash matters) against the `iss` claim of every token and the `issuer` member of the discovery document fetched from `<issuer>/.well-known/openid-configuration`."),
|
|
55
|
+
audience: z
|
|
56
|
+
.union([z.string().min(1), z.array(z.string().min(1)).min(1)])
|
|
57
|
+
.describe("Required. The `aud` this server accepts: a token must name it (or one entry of a list). Use a dedicated API audience registered at the IdP, not a client id, so an ID token or another service's token is not accepted here."),
|
|
58
|
+
clientId: z
|
|
59
|
+
.string()
|
|
60
|
+
.min(1)
|
|
61
|
+
.optional()
|
|
62
|
+
.describe("Optional. When set, the token's `client_id` (RFC 9068) or `azp` claim must equal it, restricting access to tokens minted for that one application. Unset accepts any client of the IdP that obtains a token for `audience`."),
|
|
63
|
+
jwksUri: z
|
|
64
|
+
.string()
|
|
65
|
+
.refine((v) => isHttpsUrl(v, { allowQueryFragment: true }), {
|
|
66
|
+
message: "must be an https URL",
|
|
67
|
+
})
|
|
68
|
+
.optional()
|
|
69
|
+
.describe("Optional override for the key set location. Discovery still runs (it validates the issuer); this replaces only the discovered `jwks_uri`. https only."),
|
|
70
|
+
allowedJwksHosts: z
|
|
71
|
+
.array(z
|
|
72
|
+
.string()
|
|
73
|
+
.regex(HOSTNAME_RE, "must be a bare lowercase hostname (no scheme, port or path)"))
|
|
74
|
+
.optional()
|
|
75
|
+
.describe("Optional. Hostnames a DISCOVERED `jwks_uri` may name besides the issuer's own origin. Without it the discovered `jwks_uri` must be on the same origin as `issuer` (a discovery document cannot redirect key material to another host). Needed for IdPs that serve keys from another host (Google: `www.googleapis.com`). A listed host admits the default https port (443) only: a `jwks_uri` on another port is refused (set `jwksUri` for that). Ignored when `jwksUri` is set. It never lifts the private-network block."),
|
|
76
|
+
allowPrivateNetwork: z
|
|
77
|
+
.boolean()
|
|
78
|
+
.default(false)
|
|
79
|
+
.describe("Default false: the issuer and the JWKS host are resolved before every fetch and refused when any address is loopback, link-local (including the cloud metadata address), private (RFC 1918, unique-local) or otherwise non-public. Set true only for a self-hosted identity provider on a LAN or loopback."),
|
|
80
|
+
allowedAlgs: z
|
|
81
|
+
.array(z.enum(OIDC_ALLOWED_ALGS))
|
|
82
|
+
.min(1)
|
|
83
|
+
.default(["RS256", "ES256", "EdDSA"])
|
|
84
|
+
.describe("Signature algorithms accepted, asymmetric only: HS256/384/512 and `none` are not valid values. Taken from this list, never from the token header alone."),
|
|
85
|
+
clockToleranceSeconds: z
|
|
86
|
+
.number()
|
|
87
|
+
.int()
|
|
88
|
+
.min(0)
|
|
89
|
+
.max(300)
|
|
90
|
+
.default(30)
|
|
91
|
+
.describe("Clock skew tolerated when checking `exp`, `nbf` and `iat`, in seconds. Default 30, maximum 300."),
|
|
92
|
+
discoveryCacheSeconds: z
|
|
93
|
+
.number()
|
|
94
|
+
.int()
|
|
95
|
+
.min(60)
|
|
96
|
+
.max(86400)
|
|
97
|
+
.default(3600)
|
|
98
|
+
.describe("How long the discovery document is trusted before it is fetched again, in seconds (60 to 86400, default 3600). A refresh that fails or returns a different issuer refuses tokens rather than serving a stale document."),
|
|
99
|
+
requireAtJwtType: z
|
|
100
|
+
.boolean()
|
|
101
|
+
.default(false)
|
|
102
|
+
.describe("Require the JOSE `typ` header to be `at+jwt` (RFC 9068), rejecting `JWT` and a missing `typ`. Default false, because several IdPs emit pre-RFC-9068 access tokens (Entra and Keycloak send `JWT`); with it off, `at+jwt`, `JWT`, `Bearer` and an absent `typ` are accepted and every other header `typ` (for example `id_token+jwt`) is refused. Independently of this flag, a token that names itself a non-access token is refused: a payload `typ` other than `Bearer`/`Access` (Keycloak ID and Refresh tokens), a `nonce`, `at_hash` or `c_hash` claim (ID-token markers), or a `token_use` other than `access` (Cognito). Turn the flag on when your IdP issues RFC 9068 tokens."),
|
|
103
|
+
claimMapping: z
|
|
104
|
+
.strictObject({
|
|
105
|
+
subject: ClaimPathSchema.default("sub").describe("Claim naming the caller. Required on every token."),
|
|
106
|
+
scopes: ClaimPathSchema.default("scope").describe("Claim holding the granted scopes: a space-delimited string (`scope`, `scp`) or an array of strings (`permissions`, `realm_access.roles`). Only this claim grants scopes, and only FULLY-QUALIFIED values (`read:notes`, `write:*`) are taken from it: a bare value such as the role `admin` is dropped (and logged once) unless `scopeMap` maps it, because obsidian-tc reads a bare family name as a wildcard over every resource."),
|
|
107
|
+
scopeMap: z
|
|
108
|
+
.record(z.string().min(1), z
|
|
109
|
+
.array(z.string().refine(isQualifiedScope, {
|
|
110
|
+
message: "must be a fully-qualified scope: a family (read, write, delete, execute, admin, bulk), a colon, a resource (for example `read:notes`)",
|
|
111
|
+
}))
|
|
112
|
+
.min(1))
|
|
113
|
+
.optional()
|
|
114
|
+
.describe('Optional. Maps a value of the scopes claim (an IdP role or group name) to the obsidian-tc scopes it grants, e.g. `{ vault_admin: ["admin:auth"], reader: ["read:notes", "read:search"] }`. A mapped value grants exactly its list; an unmapped, non-qualified value grants nothing.'),
|
|
115
|
+
principal: ClaimPathSchema.optional().describe("Optional claim used as the caller label in audit and logs instead of the subject (for example `email`). It does not affect authentication."),
|
|
116
|
+
vault: ClaimPathSchema.optional().describe("Optional claim binding the caller to one vault, as the `vault` claim does in jwt mode. Requires `allowedVaults`: a value outside it refuses the token. Unset: no vault claim is read."),
|
|
117
|
+
allowedVaults: z
|
|
118
|
+
.array(z.string().min(1))
|
|
119
|
+
.optional()
|
|
120
|
+
.describe("Vault ids the `vault` claim may name. Required when `vault` is set; a token naming any other vault (or a non-string) is refused, not ignored."),
|
|
121
|
+
persona: ClaimPathSchema.optional().describe("Optional claim naming a configured persona, as the `persona` claim does in jwt mode (the persona's scopes replace the token's). Requires `allowedPersonas`. Unset: no persona claim is read."),
|
|
122
|
+
allowedPersonas: z
|
|
123
|
+
.array(z.string().min(1))
|
|
124
|
+
.optional()
|
|
125
|
+
.describe("Persona names the `persona` claim may name. Required when `persona` is set; a token naming any other persona (or a non-string) is refused, not ignored."),
|
|
126
|
+
})
|
|
127
|
+
.superRefine((m, ctx) => {
|
|
128
|
+
// A persona or vault claim is a bearer capability (a persona REPLACES the token's scopes; a
|
|
129
|
+
// vault claim picks the vault): reading one without an operator-written allowlist would let
|
|
130
|
+
// the IdP's claim, not the operator, decide what exists.
|
|
131
|
+
if (m.persona !== undefined &&
|
|
132
|
+
(m.allowedPersonas === undefined || m.allowedPersonas.length === 0)) {
|
|
133
|
+
ctx.addIssue({
|
|
134
|
+
code: z.ZodIssueCode.custom,
|
|
135
|
+
path: ["allowedPersonas"],
|
|
136
|
+
message: "claimMapping.persona is set: list the persona names it may carry in claimMapping.allowedPersonas",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
if (m.vault !== undefined &&
|
|
140
|
+
(m.allowedVaults === undefined || m.allowedVaults.length === 0)) {
|
|
141
|
+
ctx.addIssue({
|
|
142
|
+
code: z.ZodIssueCode.custom,
|
|
143
|
+
path: ["allowedVaults"],
|
|
144
|
+
message: "claimMapping.vault is set: list the vault ids it may carry in claimMapping.allowedVaults",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
.default({ subject: "sub", scopes: "scope" })
|
|
149
|
+
.describe('Where the identity comes from. A string value is a claim name or a dotted path into NESTED claims (`realm_access.roles`); a claim whose own name contains dots (a namespaced URL) is written as an array of literal segments, `["https://app.example.com/roles"]`.'),
|
|
150
|
+
requiredClaims: z
|
|
151
|
+
.union([
|
|
152
|
+
z.array(ClaimPathSchema),
|
|
153
|
+
z
|
|
154
|
+
.record(z.string().min(1), z.union([z.string(), z.number(), z.boolean()]))
|
|
155
|
+
.refine((r) => Object.keys(r).length > 0, { message: "must name at least one claim" }),
|
|
156
|
+
])
|
|
157
|
+
.optional()
|
|
158
|
+
.describe('Extra claims a token must carry. An array of claim names requires a truthy, non-empty value (`null`, `false`, `0`, `""`, `[]` and `{}` fail, so `email_verified: false` does not satisfy `["email_verified"]`). An object maps a claim to the exact value it must have (`{ email_verified: true, hd: "example.com" }`; an array claim satisfies it when it contains the value). `exp`, `iat`, `iss`, `aud` and the subject claim are always required.'),
|
|
159
|
+
})
|
|
160
|
+
.describe("OpenID Connect provider whose access tokens this server verifies (`auth.mode: oidc`). Discovery is fetched at boot; the server refuses to start if it fails.");
|
|
161
|
+
export const AuthConfigSchema = z.object({
|
|
11
162
|
mode: z
|
|
12
|
-
.enum(["none", "jwt"])
|
|
163
|
+
.enum(["none", "jwt", "oidc"])
|
|
13
164
|
.default("none")
|
|
14
|
-
.describe("Authentication mode. `none` grants every request full wildcard scopes and is refused on a non-loopback HTTP bind; `jwt`
|
|
165
|
+
.describe("Authentication mode. `none` grants every request full wildcard scopes and is refused on a non-loopback HTTP bind; `jwt` needs a signing key: a jwtSecret, a JWKS, or a key in the auth registry (`obsidian-tc auth rotate-key`). A `jwt` server with none of them refuses to start. `oidc` verifies access tokens from an external OpenID Connect provider configured in `auth.oidc`; discovery runs at boot and a failure refuses to start."),
|
|
166
|
+
oidc: OidcConfigSchema.optional().describe("Required when `mode` is `oidc`, and refused under any other mode. Replaces jwtSecret/jwks*/issuer/audience/algorithms for verification."),
|
|
15
167
|
jwtSecret: z
|
|
16
168
|
.string()
|
|
17
169
|
.min(32)
|
|
18
170
|
.optional()
|
|
19
|
-
.describe("Shared secret for HS256 verification, minimum 32 characters. Secret. HS256 tokens verify ONLY against this, never against the JWKS."),
|
|
171
|
+
.describe("Shared secret for HS256 verification, minimum 32 characters. Secret. HS256 tokens verify ONLY against this (or an HS256 key in the auth registry), never against the JWKS. It is the initial signing key (kid `config`); once `auth rotate-key` has retired that key it is no longer used to verify or mint, and can be removed (`obsidian-tc doctor` says when)."),
|
|
20
172
|
tokenTtlSeconds: z
|
|
21
173
|
.number()
|
|
22
174
|
.int()
|
|
23
175
|
.positive()
|
|
24
176
|
.default(86400)
|
|
25
177
|
.describe("Maximum accepted token AGE in seconds, measured from the token's `iat`. This caps age INDEPENDENTLY of `exp`: a token with a one-year expiry is still rejected once it is older than this, so a long-lived credential needs this raised to match."),
|
|
178
|
+
// Rotation grace window: how long `auth rotate-key` keeps the PREVIOUS key verifying when
|
|
179
|
+
// `--grace` is not given. 0 (default) preserves the immediate retirement that existed before
|
|
180
|
+
// this key did. Capped at 7 days (604800; mirrored as MAX_ROTATION_GRACE_SECONDS in
|
|
181
|
+
// server/src/auth/signing-keys.ts): a retiring key still verifies everything it ever signed, so
|
|
182
|
+
// a longer window is a key that is never really rotated.
|
|
183
|
+
rotationGraceSeconds: z
|
|
184
|
+
.number()
|
|
185
|
+
.int()
|
|
186
|
+
.min(0)
|
|
187
|
+
.max(604800)
|
|
188
|
+
.default(0)
|
|
189
|
+
.describe("Seconds the previous signing key keeps verifying after `obsidian-tc auth rotate-key`, used when `--grace` is not passed (an explicit `--grace` always wins, including `--grace 0`). 0, the default, retires the previous key immediately. Maximum 604800 (7 days). A retiring key stops verifying at its `retire_after` whether or not anything has rewritten its state, so the window is exact; `obsidian-tc doctor` lists each retiring key with its time remaining."),
|
|
190
|
+
requireJti: z
|
|
191
|
+
.boolean()
|
|
192
|
+
.default(false)
|
|
193
|
+
.describe('Reject any bearer token that carries no `jti` claim, on every verify path (HS256, JWKS, `/metrics`). A jti-less token cannot be revoked individually — only rotating its signing key kills it. Default false for compatibility with tokens minted before the registry, but `securityProfile: "hardened"` sets it true (an explicit value still wins); the default itself is planned to flip to true at the next major release. `obsidian-tc doctor` recommends true once `auth rotate-key` or `token mint` has initialised the registry (`token mint` always sets a jti).'),
|
|
26
194
|
// THE-297 — asymmetric verification (RS256/ES256/EdDSA) behind the TokenVerifier seam.
|
|
27
195
|
// `jwks` is an inline JWKS document; `jwksFile` a path loaded once at transport boot (file
|
|
28
196
|
// or inline only — no URL fetch: no new network attack surface). Key rotation = multiple
|
|
@@ -57,7 +225,7 @@ export const AuthConfigSchema = z
|
|
|
57
225
|
algorithms: z
|
|
58
226
|
.array(z.string())
|
|
59
227
|
.optional()
|
|
60
|
-
.describe(
|
|
228
|
+
.describe('Explicit allowlist of accepted JWT algorithms, applied to every verify path: HS256 (the configured secret and registry keys), registry asymmetric keys, the JWKS and `/metrics`. Leaving HS256 out refuses HS256 tokens everywhere (`["EdDSA"]` is asymmetric-only). Absent, HS256 plus RS256/ES256/EdDSA are accepted. Algorithm confusion is structurally impossible regardless: HS256 verifies only against jwtSecret (or an HS256 registry key) and asymmetric algorithms only against the JWKS. Not used under `mode: oidc`, which has its own `oidc.allowedAlgs`.'),
|
|
61
229
|
// THE-456 — audience/issuer binding. When set, the JWT verifier enforces them (jose rejects a
|
|
62
230
|
// token whose `aud`/`iss` does not match), closing the confused-deputy / token-passthrough gap
|
|
63
231
|
// the MCP 2025-11-25 authorization spec requires of a protected resource. `audience` defaults to
|
|
@@ -94,17 +262,15 @@ export const AuthConfigSchema = z
|
|
|
94
262
|
.array(z.string())
|
|
95
263
|
.optional()
|
|
96
264
|
.describe("Scopes advertised as supported in the Protected Resource Metadata document."),
|
|
97
|
-
})
|
|
98
|
-
.refine((c) => c.mode !== "jwt" || !!c.jwtSecret || !!c.jwks || !!c.jwksFile || !!c.jwksUri, {
|
|
99
|
-
message: "auth.mode 'jwt' requires jwtSecret (>=32 chars) or a JWKS (jwks / jwksFile / jwksUri)",
|
|
100
|
-
path: ["jwtSecret"],
|
|
101
265
|
});
|
|
102
266
|
export const AclRuleSchema = z.object({
|
|
103
|
-
glob:
|
|
267
|
+
glob: aclPathGlob()
|
|
268
|
+
.min(1)
|
|
269
|
+
.describe("Glob matched against the vault-relative note path. Written with forward slashes; a backslash is read as a separator, so `notes\\private\\**` is `notes/private/**` (a vault path can never contain a backslash). Must be vault-relative: a leading separator, a drive letter (`C:`), a UNC prefix, a leading `./` or a trailing separator is refused at load, because such a rule can never match a note and its scopes would be silently bypassed."),
|
|
104
270
|
scopes: z
|
|
105
271
|
.array(z.string())
|
|
106
272
|
.default([])
|
|
107
|
-
.describe("Scopes REQUIRED to operate on paths matching this rule (P1.4): a caller must hold every listed scope, in addition to the tool's own required scopes, to read/write/delete a matching path. The LAST matching rule wins, replacing rather than merging the scopes of earlier matches. An empty list adds no requirement. Enforced at dispatch on tool operations
|
|
273
|
+
.describe("Scopes REQUIRED to operate on paths matching this rule (P1.4): a caller must hold every listed scope, in addition to the tool's own required scopes, to read/write/delete a matching path. The LAST matching rule wins, replacing rather than merging the scopes of earlier matches. An empty list adds no requirement. Enforced at dispatch on tool operations, and applied to results too: a search, listing or graph result for a path whose scopes the caller lacks is dropped, exactly as a path outside readPaths is."),
|
|
108
274
|
});
|
|
109
275
|
export const AclConfigSchema = z.object({
|
|
110
276
|
readOnly: z
|
|
@@ -124,17 +290,17 @@ export const AclConfigSchema = z.object({
|
|
|
124
290
|
// when present it is a glob whitelist — a path must match at least one entry.
|
|
125
291
|
// camelCase mirrors the rest of the config (readOnly, defaultScopes).
|
|
126
292
|
readPaths: z
|
|
127
|
-
.array(
|
|
293
|
+
.array(aclPathGlob())
|
|
128
294
|
.optional()
|
|
129
|
-
.describe("Glob whitelist for reads: a path must match at least one entry. Omitted leaves reads unrestricted (see strictReadDefault)."),
|
|
295
|
+
.describe("Glob whitelist for reads: a path must match at least one entry. Omitted leaves reads unrestricted (see strictReadDefault). Write separators as `/`: a backslash is read as a separator (`notes\\**` is `notes/**`), since a vault path can never contain one, and repeated separators collapse. Entries must be vault-relative: a leading separator, drive letter, UNC prefix, leading `./` or trailing separator is refused at load."),
|
|
130
296
|
writePaths: z
|
|
131
|
-
.array(
|
|
297
|
+
.array(aclPathGlob())
|
|
132
298
|
.optional()
|
|
133
|
-
.describe("Glob whitelist for writes: a path must match at least one entry. Omitted leaves writes unrestricted."),
|
|
299
|
+
.describe("Glob whitelist for writes: a path must match at least one entry. Omitted leaves writes unrestricted. Write separators as `/`: a backslash is read as a separator (`notes\\**` is `notes/**`), since a vault path can never contain one, and repeated separators collapse. Entries must be vault-relative: a leading separator, drive letter, UNC prefix, leading `./` or trailing separator is refused at load."),
|
|
134
300
|
deletePaths: z
|
|
135
|
-
.array(
|
|
301
|
+
.array(aclPathGlob())
|
|
136
302
|
.optional()
|
|
137
|
-
.describe("Glob whitelist for deletes: a path must match at least one entry. Omitted leaves deletes unrestricted."),
|
|
303
|
+
.describe("Glob whitelist for deletes: a path must match at least one entry. Omitted leaves deletes unrestricted. Write separators as `/`: a backslash is read as a separator (`notes\\**` is `notes/**`), since a vault path can never contain one, and repeated separators collapse. Entries must be vault-relative: a leading separator, drive letter, UNC prefix, leading `./` or trailing separator is refused at load."),
|
|
138
304
|
/** When true, an UNDEFINED readPaths whitelist fails CLOSED on the request path (read_note et
|
|
139
305
|
* al.), not just bridge enumeration (THE-268). Default false = M0 allow-all back-compat. */
|
|
140
306
|
strictReadDefault: z
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-acl.schema.js","sourceRoot":"","sources":["../../src/config/auth-acl.schema.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,2FAA2F;AAC3F,EAAE;AACF,oFAAoF;AACpF,8FAA8F;AAC9F,0FAA0F;AAC1F,0EAA0E;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-acl.schema.js","sourceRoot":"","sources":["../../src/config/auth-acl.schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.schema.d.ts","sourceRoot":"","sources":["../../src/config/network.schema.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,mBAAmB;;iBAoB5B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// The root `network` block: connection policy shared by every outbound provider client (the
|
|
2
|
+
// inference gateway, embeddings, reranker, model tier, plur, the Obsidian bridge, telemetry). Leaf
|
|
3
|
+
// schema, same shape as gateway.schema.ts. The enforcement lives in server/src/gateway/plain-http.ts.
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { normalizePlainHttpHost } from "../net-host";
|
|
6
|
+
export const NetworkConfigSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
plainHttpHosts: z
|
|
9
|
+
.array(z.string())
|
|
10
|
+
.default([])
|
|
11
|
+
.describe('Exact hostnames a plain http:// provider URL may name: gateway.baseUrl / OBSIDIAN_TC_GATEWAY_URL, embeddings.baseUrl, embeddings.modelTier.*.baseUrl, reranker.baseUrl, plur.endpoint, a vault\'s restApiUrl. Case-insensitive, IDNA/punycode-normalized; no wildcards, ports or paths. One list for all of them (the two TypeSafe judge blocks keep their own plainHttpHosts, which config load enforces). https:// and loopback need no entry. A listed host is still refused when a request is sent unless EVERY address it resolves to is loopback, RFC1918 (10/8, 172.16/12, 192.168/16) or IPv6 unique-local (fc00::/7): a public address, link-local, the 169.254.169.254 cloud metadata address and the other cloud metadata addresses (100.100.100.200, fd00:ec2::254, fd00:64:64:64::254, fd20:ce::254) are never allowed, listed or not. Requests go straight to that checked address (never through HTTP_PROXY / ALL_PROXY) with the original Host header, and redirects are refused (an https:// answer that redirects is refused too, so a 307/308 can never replay the key and vault text elsewhere). DEPRECATED, removed at the next major release: until then a host that is NOT listed but resolves only to private addresses still works, with a warning that names it (`obsidian-tc doctor` and `server_health` report it); list it here to silence the warning. Intended for a gateway on a host-local docker network or an encrypted overlay, e.g. `["litellm"]` for `http://litellm:4000`. The bearer key and vault text still travel in clear over that link.' +
|
|
12
|
+
" A LISTED host may also resolve to the Tailscale/CGNAT range 100.64.0.0/10 (list only a host that is a tailnet peer: Tailscale traffic is WireGuard-encrypted, ISP CGNAT space is not); an unlisted host in that range is refused, with no deprecation path."),
|
|
13
|
+
})
|
|
14
|
+
.superRefine((c, ctx) => {
|
|
15
|
+
c.plainHttpHosts.forEach((entry, i) => {
|
|
16
|
+
if (normalizePlainHttpHost(entry) === undefined) {
|
|
17
|
+
ctx.addIssue({
|
|
18
|
+
code: "custom",
|
|
19
|
+
path: ["plainHttpHosts", i],
|
|
20
|
+
message: `network.plainHttpHosts entry ${JSON.stringify(entry)} is not an exact hostname — wildcards, ports, paths, userinfo and schemes are not accepted.`,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
//# sourceMappingURL=network.schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.schema.js","sourceRoot":"","sources":["../../src/config/network.schema.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,mGAAmG;AACnG,sGAAsG;AACtG,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC;KACjC,MAAM,CAAC;IACN,cAAc,EAAE,CAAC;SACd,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,OAAO,CAAC,EAAE,CAAC;SACX,QAAQ,CACP,u/CAAu/C;QACr/C,8PAA8P,CACjQ;CACJ,CAAC;KACD,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;IACtB,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QACpC,IAAI,sBAAsB,CAAC,KAAK,CAAC,KAAK,SAAS,EAAE,CAAC;YAChD,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,gBAAgB,EAAE,CAAC,CAAC;gBAC3B,OAAO,EAAE,gCAAgC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,6FAA6F;aAC5J,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -3,6 +3,11 @@ export declare const ObservabilityConfigSchema: z.ZodObject<{
|
|
|
3
3
|
otel: z.ZodPrefault<z.ZodObject<{
|
|
4
4
|
endpoint: z.ZodOptional<z.ZodString>;
|
|
5
5
|
headers: z.ZodPrefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
6
|
+
detail: z.ZodDefault<z.ZodEnum<{
|
|
7
|
+
children: "children";
|
|
8
|
+
root: "root";
|
|
9
|
+
verbose: "verbose";
|
|
10
|
+
}>>;
|
|
6
11
|
}, z.core.$strip>>;
|
|
7
12
|
prometheus: z.ZodPrefault<z.ZodObject<{
|
|
8
13
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -17,12 +22,22 @@ export declare const ObservabilityConfigSchema: z.ZodObject<{
|
|
|
17
22
|
retention: z.ZodPrefault<z.ZodObject<{
|
|
18
23
|
eventLogDays: z.ZodDefault<z.ZodNumber>;
|
|
19
24
|
tracesDays: z.ZodDefault<z.ZodNumber>;
|
|
25
|
+
spoolRetentionDays: z.ZodDefault<z.ZodNumber>;
|
|
26
|
+
spoolMaxBytes: z.ZodOptional<z.ZodNumber>;
|
|
20
27
|
}, z.core.$strip>>;
|
|
21
28
|
}, z.core.$strip>;
|
|
22
29
|
export declare const SnapshotsConfigSchema: z.ZodPrefault<z.ZodObject<{
|
|
23
30
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
24
31
|
retention: z.ZodDefault<z.ZodNumber>;
|
|
25
32
|
}, z.core.$strip>>;
|
|
33
|
+
export declare const MemoryOrphansConfigSchema: z.ZodPrefault<z.ZodObject<{
|
|
34
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
35
|
+
intervalMs: z.ZodDefault<z.ZodNumber>;
|
|
36
|
+
batchSize: z.ZodDefault<z.ZodNumber>;
|
|
37
|
+
retiredRetentionDays: z.ZodOptional<z.ZodNumber>;
|
|
38
|
+
removedVaultRetentionDays: z.ZodOptional<z.ZodNumber>;
|
|
39
|
+
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
40
|
+
}, z.core.$strip>>;
|
|
26
41
|
export declare const MaintenanceConfigSchema: z.ZodPrefault<z.ZodObject<{
|
|
27
42
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
28
43
|
intervalMinutes: z.ZodDefault<z.ZodNumber>;
|
|
@@ -31,6 +46,27 @@ export declare const MaintenanceConfigSchema: z.ZodPrefault<z.ZodObject<{
|
|
|
31
46
|
episodesRetentionDays: z.ZodDefault<z.ZodNumber>;
|
|
32
47
|
retrievalsRetentionDays: z.ZodDefault<z.ZodNumber>;
|
|
33
48
|
jobsFailedRetentionDays: z.ZodDefault<z.ZodNumber>;
|
|
49
|
+
captureQueueRetentionDays: z.ZodDefault<z.ZodNumber>;
|
|
50
|
+
memoryOrphans: z.ZodPrefault<z.ZodObject<{
|
|
51
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
52
|
+
intervalMs: z.ZodDefault<z.ZodNumber>;
|
|
53
|
+
batchSize: z.ZodDefault<z.ZodNumber>;
|
|
54
|
+
retiredRetentionDays: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
removedVaultRetentionDays: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
57
|
+
}, z.core.$strip>>;
|
|
58
|
+
wikiLint: z.ZodPrefault<z.ZodObject<{
|
|
59
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
60
|
+
intervalHours: z.ZodDefault<z.ZodNumber>;
|
|
61
|
+
folder: z.ZodOptional<z.ZodString>;
|
|
62
|
+
maxNotes: z.ZodDefault<z.ZodNumber>;
|
|
63
|
+
judge: z.ZodDefault<z.ZodBoolean>;
|
|
64
|
+
judgeMaxCalls: z.ZodDefault<z.ZodNumber>;
|
|
65
|
+
}, z.core.$strip>>;
|
|
66
|
+
wikiPages: z.ZodPrefault<z.ZodObject<{
|
|
67
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
68
|
+
intervalHours: z.ZodDefault<z.ZodNumber>;
|
|
69
|
+
}, z.core.$strip>>;
|
|
34
70
|
}, z.core.$strip>>;
|
|
35
71
|
export declare const WatchConfigSchema: z.ZodPrefault<z.ZodObject<{
|
|
36
72
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observability.schema.d.ts","sourceRoot":"","sources":["../../src/config/observability.schema.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"observability.schema.d.ts","sourceRoot":"","sources":["../../src/config/observability.schema.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqGpC,CAAC;AAGH,eAAO,MAAM,qBAAqB;;;kBAgBnB,CAAC;AAKhB,eAAO,MAAM,yBAAyB;;;;;;;kBA8CvB,CAAC;AAyEhB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAuFrB,CAAC;AAOhB,eAAO,MAAM,iBAAiB;;;kBAiBf,CAAC;AAWhB,eAAO,MAAM,qBAAqB;;kBAWnB,CAAC;AAgBhB,eAAO,MAAM,iBAAiB;;;;;;kBAuCf,CAAC;AAUhB;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAIrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOvE;AAED,eAAO,MAAM,kBAAkB;;kBAchB,CAAC;AAUhB,eAAO,MAAM,gBAAgB;;;;;;iBAgC3B,CAAC;AAOH,eAAO,MAAM,oBAAoB;;iBAO/B,CAAC;AASH,eAAO,MAAM,oBAAoB;;iBAO/B,CAAC;AAIH,eAAO,MAAM,qBAAqB;;;;;kBAqFnB,CAAC"}
|