docs: rework the OIDC documentation and split the OIDC and RBAC guides into sections - #522
docs: rework the OIDC documentation and split the OIDC and RBAC guides into sections#522glasstiger wants to merge 55 commits into
Conversation
Compared the `acl.oidc.*` keys in EntPropertyKey against the OIDC configuration page. Five options were missing and one was documented under a name the server does not recognise. Added: - `acl.oidc.state.required` - `acl.oidc.device.authorization.endpoint` - `acl.oidc.public.keys.expiry` - `acl.oidc.response.buffer.size` - `acl.oidc.string.pool.capacity` Corrected: - `acl.oidc.pkce.enabled` does not exist, the property is `acl.oidc.pkce.required` - `acl.oidc.groups.claim` has no default, it is mandatory when OIDC is enabled - setting both `acl.oidc.host` and `acl.oidc.configuration.url` fails server startup, they are mutually exclusive Also moved `acl.oidc.cache.ttl` out of the claims section into a new "Caching and buffers" section, next to the other caching and buffer settings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
🚀 Build success! Latest successful preview: https://preview-522--questdb-documentation.netlify.app/docs/ Commit SHA: ef66a91
|
Some Identity Providers require the `state` parameter in the authorization request. Documented this in the Authentication and Authorization Flow walkthrough, next to the authorization code request, pointing at `acl.oidc.state.required`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The audit of `acl.oidc.*` covered defaults but not the validation which runs alongside them in EntPropServerConfiguration. Four constraints abort startup and none of them were documented: - OIDC and the Basic Auth Realm cannot both be enabled - the keystore path and password must be set together, or neither - `acl.oidc.tls.enabled` must match the scheme of every provider URL, including the endpoints discovered from the configuration document - `acl.oidc.enabled` needs `acl.oidc.client.id`, `acl.oidc.groups.claim` and one of `acl.oidc.host` / `acl.oidc.configuration.url` The last one replaces "several other configuration options must also be set", which left the reader to reconstruct the mandatory set by reading all 29 entries. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Setting `acl.oidc.configuration.url` makes the whole Endpoints section inert. PropOidcConfiguration only builds endpoints from the individual properties when no configuration URL is set, and init() then overwrites all five from the discovered document. `acl.oidc.port` goes the same way, as host and port come from the discovered URLs. The page implied the opposite, and only in the device authorization entry: "resolved automatically if acl.oidc.configuration.url is set" reads as though setting the property is the alternative to discovery. It is not, and the reader who tries it gets no log line saying so. State the rule under `acl.oidc.configuration.url`, where a reader meets it first, and again at the top of the Endpoints section. Also fixes the comma splice in the `acl.oidc.host` entry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`acl.oidc.device.authorization.endpoint` named a flow that appeared nowhere else in the docs. A reader who needed it found the knob and nothing else: no definition, no link, no explanation of who consumes the endpoint. The gap sat exactly where the flow belongs. The guide points CLI and standalone clients at the Resource Owner Password Credentials flow, which the same page flags as "legacy, and should be used as a last resort", and the client pages tell developers to acquire a token out-of-band without saying how. Describe the flow, state that QuestDB advertises the endpoint rather than running the flow, and cover both ways the endpoint is resolved. Add a tip in the CLI section so the reader is not steered to ROPC by default. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two claims in the entry did not hold. "The Device Code Flow is unavailable if the endpoint is neither configured nor discovered" describes a gate the server does not have. QuestDB has no Device Code Flow implementation; the endpoint's only use is exportConfiguration, which publishes it to clients. A client that knows its provider's device endpoint runs the flow either way, and QuestDB validates the resulting token as usual. "It is resolved automatically if acl.oidc.configuration.url is set" presented discovery as specific to this endpoint and implied the property is the fallback when a provider does not advertise one. Discovery covers all five endpoints, and in that mode this property is not read at all. The general rule now lives under `acl.oidc.configuration.url`, so the entry no longer needs to half-state it. Also links the flow to its new section in the guide, and adds the comma after the fronted adverbial. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`acl.oidc.public.keys.expiry` is read with getMillis, so `2m` and `120s` work. `acl.oidc.cache.ttl` and `acl.oidc.http.timeout` are read with getInt, which rejects anything but digits and turns it into a ServerConfigurationException. All three said only "in milliseconds". Grouping cache.ttl and public.keys.expiry under the new "Caching and buffers" heading put the two adjacent, which makes the identical wording worse: `acl.oidc.public.keys.expiry=30s` starts, `acl.oidc.cache.ttl=30s` does not. While in the cache.ttl entry, document that `0` disables the user info cache. isOidcCacheEnabled() tests `cacheTtl > 0` and swaps in a no-op cache. That is the knob for making a revoked account take effect immediately, which the guide discusses without naming a value. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The two pages disagreed on when to set `acl.oidc.state.required`. The reference page said to enable it if the provider "supports" the `state` parameter; the guide said to enable it if the provider "requires" it. Effectively every provider supports `state`, so the reference page's rule resolved to "always enable it", which its own default of `false` contradicts. Both rules are half the answer: enable it when the provider requires it, and enabling it is a reasonable defence in depth otherwise. Say that once, and drop the mechanism description the guide already carries. The entry also read as though the server enforces something. It does not - isStateRequired() has one consumer, exportConfiguration - so name the Web Console as the component which generates and checks the value, and link to the step in the guide that walks through it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guide said the user "is authenticated, they have no permissions at all". There is no such state. OidcClientImpl.verifyTokenSlow returns false when the sub claim is empty or the groups list is empty, which propagates to a bare 401. The empty sub claim was not mentioned at all. The distinction matters when reading logs: a 401 sends an operator looking at tokens, clocks and JWKS, while the actual cause is a group claim that stopped populating on the provider side. The passage also said only that the claim name "is configurable in QuestDB" without naming the property. Now that `acl.oidc.groups.claim` is documented as mandatory with no default, name it and link it, so the guide and the reference page agree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The PingFederate walkthrough contains no `acl.oidc.*` settings at all. It ends with "To test, head to http://localhost:9000 and login. If all has been wired up well, then login will succeed", having never told the reader to enable OIDC or point QuestDB at the provider. It worked as a narrative because the reference page's endpoint defaults are Ping-shaped and the page also claimed `acl.oidc.groups.claim` defaulted to `groups` - the exact claim name this walkthrough builds. The property has had no default since the OIDC client was introduced, so the implied recipe never started a server. Correcting the default made the omission explicit: the reference page now says the property is mandatory and the walkthrough still does not mention it. Give the section the same `server.conf` block the Entra ID section has, and note that the endpoint defaults cover PingFederate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"as a defence against" was the only instance of British "defence" in the docs, which otherwise favor American spelling. Reword rather than respell, which also tightens the sentence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two headings called "QuestDB configuration" left the slug of the older Entra ID one to the newly added PingFederate section, silently sending every existing link to /docs/security/oidc/#questdb-configuration to the wrong provider. Pin the Entra ID slug so it keeps the anchor it has always had. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The settings endpoint was named three times without being described anywhere, so a client author had no path, no response shape and no field names to work with. The only entry which mentioned it, http.context.settings, presented it as a Web Console detail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The section described the flow but gave nothing to run, and never said where the client gets the Token endpoint it polls. Add the three requests, state that there is no setting to switch the flow on, and note that the ID token is the one to take when the groups are encoded in it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Key rotation does not depend on the expiry, because a token signed with an uncached key triggers an immediate reload. The setting governs revocation. Both JWKS settings are also inert unless the groups are encoded in the token. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Authentication does fail, but only the HTTP endpoint answers with 401. PGWire returns an authentication error instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Server-side support for the Device Code Flow is not finished, the clients are still work in progress, and there are open PRs which will change the server behaviour further. Documenting it now would describe an interface which is going to move. Removes the `acl.oidc.device.authorization.endpoint` configuration entry, the Device Code Flow section of the OIDC guide with its worked example, the tip which pointed the CLI section at it, and the endpoint from the settings endpoint example. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The page said the keys are only read when the group memberships are encoded in the token. QuestDB builds the public keys store whenever OIDC is enabled, and the store downloads the keys from its constructor, so the endpoint is contacted at startup in the user info flow too. Only the use of the keys is conditional: token signatures are verified in the encoded-in-token flow. Names what the gate actually controls, and records that a failed download is logged rather than fatal, so an operator seeing that error in the log knows it is not a startup failure. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Setting the TTL to zero was documented as validating every request against the OIDC Provider. That holds only in the user info flow. When the group memberships are encoded in the token, revalidation verifies the token signature locally and never calls the provider, so disabling the cache buys no round trip and gives no revocation. The distinction matters because the Entra ID walkthrough turns that setting on, and an operator zeroing the TTL there would expect revocation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The description ended at "QuestDB Enterprise Web Console", while the page also covers PGWire token authentication, group mapping, and the settings endpoint a client reads its OIDC configuration from. The build copies title and description verbatim into llms.txt, so that sentence is the whole of what a retrieval index sees for this page, and it pointed away from most of the content. Avoids a colon in the value, which breaks the YAML front matter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add a Minimum configuration section carrying the four settings a working setup needs, and all eight conditions which make the server refuse to start. acl.oidc.enabled listed four of them and read as complete, so it now points at the section instead. The section also names acl.enabled, which OIDC needs and which neither page mentioned. Correct four entries which claimed more than the server does: - acl.oidc.pkce.required read as an enforcement switch. The server only publishes it, the same as acl.oidc.state.required, and enforces neither. Both entries now say so through a shared section intro, which also lets the state entry drop the mechanism it repeated. - acl.oidc.tls.keystore.password said the pair fails startup. The check runs only when OIDC is enabled. - acl.oidc.sub.claim read as cosmetic. An empty sub claim fails authentication, the same way a missing groups claim does. - acl.oidc.response.buffer.size advised raising it for large user info responses. The size which rejects a response is the request buffer, not this one, so the advice does not hold. It now states the accepted format, which takes K and M but no G, and what a response that does not fit looks like. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The section told any client to read the endpoints from /settings instead of hard coding them, without saying what the response guarantees. Add a table giving each key its type and when it is present, and note that a provider advertising a device authorization endpoint adds a tenth key. acl.oidc.client.id and acl.oidc.redirect.uri are published as null when unset, so the redirect URI now carries the fallback a client needs. Say to read acl.oidc.enabled first. The acl.oidc.* entries are published whether or not OIDC is enabled, and the endpoint URLs are then built from the defaults, so a client which skips the check sends users to a URL which looks real and is not. The same note names acl.enabled, which can leave acl.oidc.enabled reporting true while nothing authenticates. Correct http.context.settings, which said the Web Console reads it. The Web Console requests /settings and ignores the setting, so moving the path stops it from loading. Both pages now say to leave it alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both pages said to leave `http.context.settings` alone because changing it stops the Web Console from loading. It does not. After reading the override, PropServerConfiguration re-adds the default `http.context.web.console` + `/settings` unconditionally, under a comment saying it exists so that customization does not break the Web Console, and HttpServer binds the settings processor to every path in the set. Overriding the setting adds a path; the default keeps serving and the console keeps working. The `http.context.web.console` entry twenty-five lines below already documented this correctly, so the page contradicted itself. Keep the half that is true - the Web Console does request `/settings` and does not read the setting - and note that the property takes a comma-separated list, which neither page mentioned. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four claims on the configuration page promised more than the server delivers. `acl.oidc.cache.ttl` said that with the groups encoded in the token the token is "revalidated locally, and the provider is not contacted". Both halves were too strong. The local check covers the signature and the audience; jwt.rs sets validate_exp to false, so the token's expiry is never tested. And the JWKS store still calls the provider when the cached keys expire or the key id is unknown. `acl.oidc.sub.claim` said a missing claim fails authentication "the same way it does when the groups claim is missing" - a rule this page did not contain. `acl.oidc.groups.claim` documented only the startup requirement, never the runtime failure, and unlike its sibling carried no link to the guide. State the runtime failure on both, and let sub.claim point at groups.claim. `acl.oidc.response.buffer.size` claimed that a response which does not fit fails authentication with a logged reason. The setting sizes the HTTP client's response parser buffer; no code path ties an overflow to that outcome. Keep the generic behaviour, which does hold. The PKCE and state paragraph said the client checks both values. It generates both, but the provider checks the verifier at the token endpoint; the client checks only the state value it gets back. Also widen the page description. It stopped at "configuration settings" while the page covers startup rules, endpoints, TLS, claims, caching and buffers, and both OIDC pages share a title - in llms.txt the description is the only thing telling them apart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The section enumerated the response but left out three things a client needs. `acl.oidc.device.authorization.endpoint` was described in prose below the table and missing from the table itself, and the prose attributed it solely to a provider advertising the endpoint. In host mode the property drives it - the project's own SettingsEndpointTest is host-based and its payload carries the key. Say configured or discovered, and give it a row. `acl.enabled` is published in the same response, immediately before the acl.oidc entries. The note warned that acl.oidc.enabled can read true while access control is off, and then withheld the key that settles it. Add it to the example and the table, and tell the client to read both. That QuestDB advertises the PKCE and state flags and enforces neither was stated only on the configuration page. A client author reading this section alone would conclude the server requires PKCE. Say it here too, in the same words. Also link the settings endpoint from the two sections whose readers are its audience. Browser-based clients builds an authorization request, and non-interactive clients posts to a token endpoint it currently hard codes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every client page tells the reader to acquire a token out of band from their IdP, and links to the top of the OIDC guide, which drops them at the architecture diagram. None of them mentioned that QuestDB publishes the provider's authorization and token endpoints on its settings endpoint, so a client can discover them instead of hard coding them. These pages are the settings endpoint's audience, and it had no inbound link from any of them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The docs-changelog criteria put config properties and syntax additions to reference pages under Reference, and rewritten sections and new examples under Updated. A note added to a walkthrough step and a new server.conf example in the PingFederate walkthrough are both guide changes, not reference ones. The PingFederate entry also linked to the top of a walkthrough some 330 lines long, rather than the configuration block it describes. The entry for the four missing configuration options stays under Reference, where it belongs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review of the OIDC pages turned up one gap and three statements that do not hold. Each is verified against the Enterprise source. Which token to send. The settings endpoint publishes acl.oidc.groups.encoded.in.token to every client and never said what to do with it, yet it decides which token belongs in the Authorization header: the ID token when the groups are encoded in it, the access token otherwise. Sending the wrong one is a 401 with the reason only in the log. The Web Console already picks the token this way. Documented in a new section, in the option's own entry, and in the ingestion example. The step 7 aside claiming the token "is rather opaque and does not contain user details" is only true of the access token, so it is now scoped: the ID token is a JWT whose payload is readable base64. The settings path. Setting http.context.settings adds paths rather than moving the service, but the path that survives is the Web Console context path plus /settings, not a literal /settings. Both pages said /settings keeps working whatever else is configured, which stops being true as soon as http.context.web.console is changed, as the neighbouring entry on the same page already noted. The Web Console also requests a relative "settings" rather than an absolute path. The non-interactive example. The prose said the token endpoint does not have to be hard coded and the example below it hard coded the endpoint and the client id. It now reads them from the settings endpoint, which is the only worked demonstration of the feature on the page. acl.enabled. Disabling access control stops OIDC authentication, but it does not stop the OIDC settings from being validated: the startup rules still fire and the provider's configuration document is still downloaded, so a broken OIDC configuration still prevents the server from starting. Also documented acl.oidc.device.authorization.endpoint, which the settings table already named while the configuration page did not. QuestDB never calls it and does not implement the Device Authorization Flow, it resolves the endpoint and republishes it for clients which do, so the option is documented without documenting the flow. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With acl.oidc.groups.encoded.in.token enabled QuestDB stops calling the provider and validates the ID token on its own. That it does not check the expiry was documented, but as the closing clause of acl.oidc.cache.ttl, a caching setting nobody opens unless they are tuning the cache. The consequence belongs where the mode is switched on. The option's own entry now carries the full picture: the signature, the audience and the presence of the claims are checked, exp, nbf and iss are not, and neither acl.oidc.cache.ttl nor a shorter token lifetime in the provider shortens how long an issued token is accepted. Once issued it stays good for as long as the signing key is in QuestDB's cache. The Entra ID walkthrough turns this mode on, because Entra ID cannot serve the group memberships from its User Info endpoint, so it now warns about it as well. acl.oidc.cache.ttl keeps a short note, because someone lowering the TTL to tighten revocation needs to know it will not help here, and points at the option for the detail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The same class of imprecision the rest of this branch fixed, left behind in three entries which were not otherwise being touched. acl.oidc.audience reads as though QuestDB always checks the audience. It only does when acl.oidc.groups.encoded.in.token is true, the one case in which it validates tokens itself. In the user info flow the provider decides, and the audience is never looked at. This is the qualifier already carried by acl.oidc.public.keys.expiry and acl.oidc.public.keys.endpoint. acl.oidc.scope said the openid scope "is mandatory and must always be included", on a page which now lists what makes the server refuse to start. It is the provider that enforces it: QuestDB passes the value on without inspecting it, so omitting openid fails at the provider. acl.oidc.ropc.flow.enabled said only that it enables the flow, which undersells it. QuestDB runs the flow itself, exchanging HTTP basic and PGWire credentials for a token at the provider, which is what lets psql and similar clients log in with SSO credentials. Local users are matched first, so a local account shadows an Identity Provider one of the same name. The setting is also absent from the settings endpoint, so a client cannot discover whether the flow is available. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The three client pages which list OIDC token acquisition as unsupported tell the reader to acquire an access token out of band. That is the wrong token whenever acl.oidc.groups.encoded.in.token is set, which is what the Entra ID walkthrough requires, because Entra ID cannot serve the group memberships from its User Info endpoint. Sending the access token there fails with a 401 and the reason only in the server log. Pointing these pages at the settings endpoint gave them a link to the response which carries the answer, but not to the section which explains it, and no client page linked "Which token to send" at all. Each now links it next to the settings endpoint and asks for "a token" rather than "an access token". The Go page and the QWP ingress page had the same missing link without the wrong noun, so they get it too. Also fixes a referent: "discovering its authorization and token endpoints from QuestDB's settings endpoint" attached "its" to the IdP, while the endpoints are the ones QuestDB publishes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reading the client id and the scopes from the settings endpoint went a step too far. acl.oidc.client.id and acl.oidc.scope describe QuestDB's own registration with the provider, so the example had every ingestion job present the Web Console's OAuth2 client, against the page's own advice that each application which integrates via OIDC should be given a different Client Id. The borrowed scope is whatever QuestDB asks consent for, which under the Entra ID walkthrough is "openid profile offline_access", so the job requested a refresh token it never uses. The token endpoint stays discovered, being a server fact. Two lookups could also fail on a bare KeyError. acl.oidc.token.endpoint is absent when acl.oidc.configuration.url is set while OIDC is disabled, which is what the presence table means by "when the endpoint is resolved" and what the note above the example means by reading acl.oidc.enabled first. The example did neither, and now gates on that flag. The password grant returns an id_token only if the provider issues one for it and openid is among the requested scopes, which QuestDB does not enforce, so the example says so and fails with a diagnostic rather than a KeyError. Also replaces the manual json.loads of response.content with response.json(), and adds raise_for_status so a refused grant does not surface three lines later as a missing token. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # documentation/changelog.mdx
The ROPC example discovered the provider's token endpoint over plaintext http and then posted the user's SSO password to whatever URL came back, contradicting the rule the same page states for the device flow. Both snippets now use https against questdb.example.com, matching the device-flow examples, and a caution explains why the discovery request and the connection carrying the token both need it. "Which token to send" is the section every client page links to, but the consequence of acl.oidc.groups.encoded.in.token lived only on the configuration page and in the Entra ID walkthrough. It now carries its own caution: exp, nbf and iss go unchecked, so an expired token is still accepted and provider token lifetimes are not enforced. The validation column also named two checks where the server performs three. https for discovery was written as advice; the clients enforce it. Say so, and name the opt-out for each language. Note that Rust, C, C++ and Python accept loopback http for local development while Java rejects it, since that difference decides whether http://localhost:9000 works. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0111nSmXn2CmqkYMGobjvxgY
The four client pages sent readers to #official-client-examples for endpoint pinning, explicit configuration, persistence and error handling. That section held five copies of the same happy path and none of those things, so the pointers dead-ended. It now carries four subsections covering them, and the pointers name what is actually there. Each subsection names the API per language rather than describing it: - The sign-in prompt. The five clients behave differently and the tabs implied they did not: Java prints to System.out and performs no terminal check, so redirecting output hides the code; Rust, C and C++ refuse without a TTY; Python defers to the IPython kernel. Records the custom-prompt and force-interactive setters, and which URL is vetted for opening. - Explicit configuration. "Configure the client explicitly" appeared on five pages and named nothing. Lists what each setting is for and where the setters live, including that Java splits them between DiscoveryOptions and builder(). Notes that acl.oidc.audience is never published, so discovery cannot supply it and a provider that needs an audience requires an explicit one. - Token persistence. FileTokenStore was named once, in prose, with no wiring. Gives the per-language constructor and attachment, the default directory and its override, and the 0600/0700 permissions, so the tradeoff the page asks the reader to accept can actually be judged. - Interaction-required errors. Java has no distinct type and Rust flattens the kind to ErrorCode::AuthError, reachable only through err.oidc_error(), so the blanket claim that transports "return an interaction-required error" was wrong for two of the five clients. Also corrects DiscoveryOptions.issuer(...) on the Java page, which reads as a static call on a top-level type and is neither, and notes on all four pages that silent refresh depends on offline_access being in acl.oidc.scope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0111nSmXn2CmqkYMGobjvxgY
Six places where the docs disagreed with themselves or with the server. Auth enumerations that predate OIDC now include it. security/rbac.md counted three authentication methods and connect/compatibility/rest-api.md counted two; both are canonical pages that other pages defer to, and both omitted the credential this PR documents. rest-api.md also now says an OIDC bearer token is a different credential from the REST API token it describes below, since the two share the Authorization: Bearer header and are easy to confuse. connect/clients/connect-string.md is where the client pages send readers for the full credential grammar, and it listed only username, password and token. It now records that device-flow credentials are not connect-string keys: the auth object rides alongside, and rotates, where a static token= does not. high-availability/failover.md told operators to send "an OIDC access token". Under acl.oidc.groups.encoded.in.token, which the Entra ID walkthrough in this same PR sets to true, that token is rejected with a 401. It now names both tokens and links the rule. The Client Id rule had three answers. The architecture overview and the non-interactive section both say each application should have its own; the device-flow examples reuse QuestDB's without saying so. The examples now say what discovery costs, and step 1 states the rule once and points at the explicit-configuration section instead of gesturing at it. Go and .NET readers could reach the settings endpoint and no further: the sections they landed on pointed back at each other, and neither page linked the one section describing a flow they could implement. Both now do, and name a library that implements the device grant. Finally, the token-flow validator deserializes the payload into a fixed struct with sub and groups, so it requires those literal names and rejects a token without them whatever acl.oidc.sub.claim and acl.oidc.groups.claim say. Custom names only ever applied to the user info flow. Both option entries now say so. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0111nSmXn2CmqkYMGobjvxgY
The device flow was 350 lines with no sidebar path: reachable only by landing on the OIDC guide and scrolling to entry 16 of 32. It is now security/oidc-device-flow.mdx with its own sidebar entry, and the guide keeps a short section at #device-authorization-flow so external links still resolve. Every internal link points at the new page directly rather than through that stub. Extracting it also settles the ordering: the flow no longer sits 250 lines above the "Interactive clients" taxonomy that motivates it, and the Jupyter and CLI sections simply link out. The five examples move to partials under documentation/partials/ and are imported by both the new page and the client page for that language. They were duplicated before, and the C copy had already drifted: the guide used goto fail and printed the error, the client page used goto done and discarded it. One copy now, so they cannot diverge again. Each example also does some work. All five stopped at connect(), so the rotating token was never seen carrying a request; they now write a row to trades and flush, using the same schema and idiom as each page's quick start. The C++ example gains the qwp_reader.hpp include. Its comment advertised pool.borrow_reader(), which does not compile against the sender header alone because the return type is incomplete there. Which token to send is promoted to a top-level section. Nine pages link to it as the canonical answer for the Authorization header, and it sat as an h3 under Settings endpoint, where a reader arriving from a client page landed mid-way through a JSON response contract. The two paragraphs that really do belong to the settings endpoint - the note on reading acl.oidc.enabled first, and the http.context.settings path - move back above it. Finally, the OIDC subsection on the Python and Rust client pages was placed before the parent section's own content, so it swallowed the general TLS root-store tables and, in Python, the WebSocket-upgrade auth prose. Both now sit where the C and C++ page already put theirs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0111nSmXn2CmqkYMGobjvxgY
Factual corrections, each checked against the server or the Web Console: - acl.oidc.enabled=false does not take the settings out of play. The configuration URL is still parsed and its scheme still validated, so a malformed value fails startup with OIDC switched off. - With acl.oidc.configuration.url set and OIDC disabled, the endpoint keys are absent from the settings response rather than built from the defaults. Only the host-configured case defaults them. - The settings example and its key table omitted acl.basic.auth.realm.enabled, which the server emits between acl.enabled and acl.oidc.enabled. Both now match SettingsEndpointTest key for key and in order. - The Web Console falls back to the location it was loaded from without the query string or fragment, not window.location.href. - Step 7 of the walkthrough was generalised to either token, but the sentence about the user info cache still said "the access token". - The CLI section listed Java, Rust, C and C++ and omitted Python, which two other lists on the same page and the configuration reference include. Consistency: - The Rust crate-features table gained the oidc row it documents elsewhere. - http.context.settings said its default is /settings, four paragraphs above the prose explaining that this holds only while http.context.web.console is at its default. Qualified inline, where a config scraper will read it. - The two sidebar entries both rendered "OpenID Connect (OIDC)". They are now the guide, the device flow, and the settings reference. - nodejs.md was the one client page with no OIDC route, and its Enterprise auth link led to a page that does not mention OIDC. - The Implicit flow is marked deprecated: it cannot use PKCE, which the settings endpoint advertises as required by default. - Three same-page jumps were written as third-person cross-page references to "the OIDC operations document", which is the page the reader is on. - The two non-interactive examples used the legacy ILP Sender over http while the Python client page teaches questdb.connect over wss. - server.conf blocks were fenced as shell in three places and ini in another. - The QuestDB host placeholder was questdb.host in the older examples and questdb.example.com in the new ones; the query example also used port 9999. Nits left alone deliberately: "Device Authorization Flow" keeps its title case, matching the sibling "Authentication and Authorization Flow" and RFC 8628's own term, and renaming it would break the anchor the extraction preserved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0111nSmXn2CmqkYMGobjvxgY
The page told every reader the prompt's text fields were display-safe. That holds for Java, C and C++, which sanitize before the callback runs, but not for Rust and Python, whose built-in renderers sanitize at display time and whose custom renderers receive the provider's response verbatim. The page recommends a custom renderer for headless and notebook use, so the assurance was given exactly where it does not apply. State the split, and name the helpers that appeared nowhere in the docs: display_user_code() and display_verification_uri() in Rust, sanitize_display_text in questdb.auth. Extend the browser target sentence past C and C++, since Rust and Python expose one too. questdb::error_kind::interaction_required does not compile: the enum is declared in namespace questdb::oidc. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QUNkemSPGkrzov1yFrq6xt
Ten corrections, each checked against the client and server sources. The browser table said only Java opens a browser. All five do by default, and four expose an opt-out the page never named, so a headless host got a process nobody can see and no documented switch. The plaintext opt-out was described as a single flag. A client over plaintext also refuses settings-sourced credential endpoints, so the flag needs an issuer pin and explicit values alongside it. Say so, and say that the opt-out never relaxes the Identity Provider legs. issuer was framed as plumbing for a missing endpoint. /settings needs no authentication, so pinning the issuer is what stops a tampered response steering the sign-in, which is why it belongs against any untrusted server. Java splits the settings across DiscoveryOptions and builder(), and the builder demands the client ID and both endpoints together, so Java alone cannot combine its own registration with discovered endpoints. The page claimed parity. acl.oidc.groups.claim: the configured claim names apply in both flows, not just the user info flow. The validator additionally requires literal sub and groups in the payload. Conflating the two produced an instruction to reset acl.oidc.sub.claim which contradicted the Entra ID walkthrough on the next page. Also: rbac.md implied OIDC works on PGWire untouched, when it needs an opt-in setting; steps 8 and 9 of the walkthrough described the user info flow as if it were the only one; the PGWire section never said which token to send; the token store, endpoint pinning and interaction-required recovery had no code in any language; and the page linked to no client page at all, to no provider walkthrough, and to no TLS setting despite requiring https. Examples: ETH-USD to ETH-USDT per the demo dataset, the C partial gains the side and amount columns the other four write, and Java assigns the timestamp client-side like its siblings. Retitle the configuration page to OIDC settings, so it no longer collides with the guide in search results, and update the overview row to match. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QUNkemSPGkrzov1yFrq6xt
The feature had five names across the surfaces a reader meets it on: the page title, the sidebar, the guide's heading, the changelog and eleven link texts. Settle on "OIDC device flow", which is what people search for and what the sidebar already said, and pin the guide heading's old anchor so any external link to it still resolves. The sequence diagram drew discovery through QuestDB as the only path, though the page documents pinning an issuer instead, and stopped at the first token though the rest of the page is about what happens after it. Add the branch, run the user's approval in parallel with the polling loop as RFC 8628 does, and note the refresh. Verified with mermaid 11.9.0, the version the site ships. The four client pages sent readers to #official-client-examples while promising the sign-in prompt, the token store and explicit configuration, so the link landed on the code the reader had just finished and left the promised material several sections below. Point each at the section it names. FileTokenStore::at_default_location() returns a std::io::Result, so the `?` shown against a questdb::Result signature does not compile. Say what it returns, and why it can fail at all. Also: the token store's override is a dotted name, which most shells will not export; the ROPC example indexed a settings key the same page documents as conditional; a .env block was fenced as python and a server.conf block was not fenced as anything, in a section that is now a link target; oidc.mdx imported Tabs and TabItem without using either; and failover.md and dotnet.md carried em-dashes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QUNkemSPGkrzov1yFrq6xt
…homes Four reference sections sat as H3s under "Official client examples", so the client pages linking to them landed the reader on the code they had just read with the promised material several sections below. Promote them to H2. Their anchors are derived from the heading text, not its level, so every inbound link still resolves. Go, Node.js and .NET readers were sent here for the polling interval, slow_down and the device code's expiry, which the page only named in passing before saying the official clients handle them. Add a section that actually sets out the two requests, the four token-endpoint error codes and what each one means, and the refresh token, for anyone driving the grant themselves. offline_access was hedged three ways: "if your provider requires" on the hub, "most providers issue only when" on four client pages, each with its own wording. State it once under acl.oidc.scope, where the setting lives, name Entra ID as a provider that needs it, and say that adding it is harmless where it is not needed. The hub and the client pages now defer to that. Move the pointer to the client pages to the end of the page; it is a page-level "where next" and had been sitting under Interaction-required errors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QUNkemSPGkrzov1yFrq6xt
The next Enterprise release checks exp when QuestDB validates a token itself, so the warnings built around an expired token still being accepted no longer hold. Move exp into the Checked column and rewrite the caution around the caveat that survives: QuestDB never asks the provider about the token, so a revoked token keeps working until it expires. The only lever is withdrawing the signing key, which is what acl.oidc.public.keys.expiry already documents. The acl.oidc.cache.ttl note, the Entra ID walkthrough and the changelog carried the old claim too, and now agree. nbf and iss are still unchecked. The Jupyter notebook section opened with JupyterHub and OAuthenticator, which decide who may log in to JupyterHub and hand the notebook nothing to send to QuestDB. Lead with the device flow, which is the answer to how a notebook authenticates against QuestDB, and keep the JupyterHub point as the disclaimer it is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QUNkemSPGkrzov1yFrq6xt
Token validation: superseded by questdb-enterprise#1188Review flagged that So the table on this branch is right for the release that carries #1188, and Two things still need a pass here, because #1188 changes more than 1. The literal
That contradicts two passages this PR adds to
2. The revocation caution is still not right after #1188. It says the token
It also notes the WebSocket connection is not torn down when its token expires, Since the docs now describe post-#1188 behaviour, both PRs should land together, |
The single oidc.mdx had grown to 1600 lines covering the architecture, the sign-in walkthrough, client discovery, integration patterns, group mapping and two provider setups. Each of those is now its own page under security/oidc/, with the overview at the section root and a "where each section moved" map for the old anchors. Inbound links across the configuration, client and deployment docs are repointed at the new anchors. Also corrects the group claim rules in the configuration reference. The token validator requires sub, aud and exp and never looks at the group memberships, so acl.oidc.groups.claim may name any claim the token carries rather than being pinned to "groups", and a bare string is accepted where a provider sends a lone group. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cz9Rp9SBqLYSUsFkGFbGN
Same treatment as the OIDC page: rbac.md covered principals, authentication and endpoint permissions, granting rules, the full permissions table and the worked scenarios in one file. Each now has its own page under security/rbac/, with the overview at the section root, and every inbound anchor across the SQL reference, getting-started and operations docs repointed. Two permission statements are corrected while passing through. REBASE WAL, SWITCH COLD STORAGE ROLE and SET COLD STORAGE ROLE need the SYSTEM ADMIN permission, which is grantable, rather than unspecified "database administrator privileges" outside RBAC. all_permissions() lists every permission the database supports, not the ones currently applied. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cz9Rp9SBqLYSUsFkGFbGN
The sign-in prompt table claimed Rust, C and C++ fail with an interaction-required error when there is no terminal, and that Python fails unless the kernel accepts stdin, summarised as a container with no TTY failing outright in four of the five clients. Every one of those clients deliberately has no TTY check, and says so: a missing terminal is not evidence of a missing human, since a pipe into tee, a supervisor or an IDE capturing stderr all still show the code to someone. A headless container therefore prompts to a stream nobody reads and polls until the device code expires, which is the opposite of what the page promised its own headline audience. All five rows now say the call runs anyway. Python's one exception is stated for what it is: positive evidence from a notebook executor issuing allow_stdin=false, not a terminal check. The fail-fast lever moves out of the custom-prompt table into its own, in the direction that is useful. It read .interactive(true) for Rust and C++, which is the default and so a no-op, and would have left the same wrong model in place next to the corrected table above it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cz9Rp9SBqLYSUsFkGFbGN
Five examples across four pages either fail at runtime, fail to compile, or describe syntax the server does not have. The basic authentication example in the client integration guide calls parse.urlencode() and json.loads() while importing neither, so it raises NameError twice. Its own import list is self-contained, unlike the ROPC example above it, which continues a block that does import both. Both requests in that section posted to http://localhost:9000, one carrying an OIDC bearer token and the other base64 SSO credentials, while the caution further down the same page says both requests must go over https because a tampered plaintext response can redirect the password grant. They now use the https://questdb.example.com:9000 the rest of the page uses. The Entra ID walkthrough creates the group extUsers and then grants twice to groupName, which is never created, so running the three blocks in order fails on the second. The placeholder table names go too, for the demo tables. DROP TABLE ... CASCADE PERMISSIONS does not exist, and the default the note described is the opposite of what happens: EntDdlListener drops a table's permissions unconditionally, for tables, views, materialized views and live views alike, so a drop-and-recreate silently loses every grant. The Rust token store line used ? on at_default_location(), which returns std::io::Result. There is no From<io::Error> for questdb::Error, so it does not compile in the surrounding example's own signature. It now shows the map_err form the upstream oidc_device_auth.rs example uses, and names the infallible alternative. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cz9Rp9SBqLYSUsFkGFbGN
Turning the two guides into sidebar categories dropped both overview pages out of the generated LLM index. The category branch of generate-llms-files.js emits item.label as plain text and never reads item.link, so a category root reachable only through link: produces a heading with no URL and no description. Before the split both were type: "doc" entries and appeared as linked bullets. That silently cost the index the RBAC quick start, the access control depth tables, the only consolidated list of the ACL SQL statements, and the OIDC architecture overview. Repeating the index doc as an explicit Overview item is what the Enterprise Kubernetes Operator category already does for the same reason. It also gives each section a visible Overview row rather than leaving the root reachable only by clicking the category header, and makes the sidebar_label in both index frontmatters live instead of dead metadata. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cz9Rp9SBqLYSUsFkGFbGN
What
The OIDC documentation, in five parts: the Device Authorization Flow, which
was not documented at all and now has its own page; the settings endpoint a
client discovers the provider from; the configuration reference, which was
missing five options and described four others wrongly; a pass over the pages
elsewhere in the docs that contradicted any of it; and a restructure that
breaks the two 700-1600 line security pages into browsable sections.
Important
This is blocked on the client releases. See
Release coordination at the bottom. The device-flow
APIs documented here are correct, but none of them has shipped, and the Rust
snippet does not build against the published crate.
Device Authorization Flow
A new page, OIDC Device Authorization Flow,
for command-line applications, containers, and remote notebook kernels, where no
browser redirect can come back to the client. It has its own sidebar entry; the
guide keeps a short section at the old
#device-authorization-flowanchor soexisting links still resolve.
directly and then presents the resulting bearer token, which the sequence
diagram walks through end to end. The client is responsible for the polling
interval,
slow_down, and the device code's expiry.client registered for the application, a device authorization endpoint QuestDB
can publish (from the configuration document, or from
acl.oidc.device.authorization.endpoint), andoffline_accessinacl.oidc.scopewhere the provider requires it before issuing a refreshtoken.
client ID, scope, endpoints, and token mode from
/settings, signs in, thenwrites a row through the rotating token. They live in
documentation/partials/and are imported by both this page and the clientpage for that language, so the two copies cannot drift.
https. The clients reject plaintext ratherthan warn. Rust, C, C++ and Python make an exception for loopback; Java does
not. The per-language opt-out is documented.
Four sections cover what the examples do not show, because the client pages were
already pointing at them:
headless job works at all: Java prints to
System.outand performs noterminal check, so redirecting output hides the code; Rust, C and C++ refuse
without a TTY; Python defers to the IPython kernel. Custom-prompt and
force-interactive setters are named for each.
where the setters live, including that Java splits them between
DiscoveryOptionsandbuilder().default directory and its override, and the
0600/0700permissions, so theat-rest tradeoff the page asks the reader to accept can be judged.
the kind to
ErrorCode::AuthError, reachable only througherr.oidc_error().Each client page gained an OIDC device flow (Enterprise) section
(Java,
Python,
Rust,
C & C++). On the Python
and C/C++ pages this replaces the "Unsupported auth paths" table: OIDC token
acquisition and mid-session rotation are no longer unsupported there, so the
table is now a short list of what genuinely remains - mutual TLS, and a fixed
token=that does not rotate.The guide's Jupyter and CLI sections point at the device flow first. ROPC is
demoted to the fallback for tooling that cannot run the flow, such as
psql.Settings endpoint
The settings endpoint
is what a client reads, without authentication, to discover the authorization,
token, and device authorization endpoints, the client ID and scope, and whether
PKCE and
stateare required. The new section documents the response key bykey, which keys are omitted when unset, and two traps:
acl.oidc.enabledhas tobe read before the rest, because the
acl.oidc.*keys are published from thedefaults even when OIDC is off, and the path follows the Web Console context
path rather than being fixed at
/settings.Which token to send
is a top-level section rather than a subsection of the settings endpoint, since
nine pages link to it as the canonical answer for the
Authorizationheader. Itdocuments what
acl.oidc.groups.encoded.in.tokenmeans for a client, and nowcarries the security consequence that previously lived only in the
configuration reference: with the setting enabled QuestDB validates the token
itself and never asks the provider about it, so a revoked token keeps working
until its
exppasses.http.context.settingswas rewritten to match: it serves clients rather than only the Web Console, it
takes a comma-separated list, and setting it adds paths instead of moving the
service. The Go,
.NET,
Node.js, and
QWP ingress
pages link to it, and the first two also link the device flow, since neither
client runs one and the settings endpoint alone does not tell them how to get a
token.
Configuration reference
Compared the
acl.oidc.*keys defined inEntPropertyKey.javaagainstthe OIDC configuration page. The
server defines 29 options, the page documented 24 names, one of which the server
does not recognise. All 29 are now documented.
Added
acl.oidc.state.requiredfalsestateparameter in the Authorization Code Flow. Published to clients through the settings endpoint, same as the PKCE flag.acl.oidc.device.authorization.endpointacl.oidc.public.keys.expiry120000acl.oidc.response.buffer.size1Macl.oidc.string.pool.capacity128Corrected
acl.oidc.pkce.enableddoes not exist. The property isacl.oidc.pkce.required. There is no alias, so anyone who copied thedocumented name got silently ignored config, with PKCE staying on.
acl.oidc.groups.claimhas no default. The page claimedgroups, but thedefault is
nulland the option is mandatory when OIDC is enabled - theserver refuses to start without it.
acl.oidc.hostandacl.oidc.configuration.urlare mutually exclusive.Setting both fails server startup, which the page did not mention.
built whenever OIDC is enabled and loads the keys from its constructor. Only
the use of the keys is conditional.
authenticated but ends up with no permissions.
verifyTokenSlowreturns falsewhen either the groups or the sub claim is missing or empty, in both flows, so
the login is rejected:
401over HTTP, an authentication error on PGWire,with the reason only in the server log.
acl.oidc.groups.encoded.in.token=true. The signature validator requiressub,audandexpand checks the signature, audience and expiry againstthem; it never looks at the group memberships. QuestDB then reads the
principal and the groups out of the same payload through
acl.oidc.sub.claimandacl.oidc.groups.claim, exactly as in the user infoflow. So
acl.oidc.groups.claimmay name any claim the token carries,rolesfor example, andacl.oidc.sub.claimmay name any claim too as longas the token still carries
subfor the validator. The claim's value isnormally an array; a bare string is accepted where a provider sends a lone
group.
Scoped to what the server actually does
acl.oidc.audienceis only checked when the groups are encoded in the token.It is also the one discovery key the clients look for that the server never
publishes, so a provider that requires an audience needs it set on the client.
openidinacl.oidc.scopeis enforced by the provider, not atstartup.
acl.oidc.ropc.flow.enabledmakes QuestDB itself exchange HTTP basic andPGWire credentials for a token at the provider.
the expiry and the presence of the claims - but not
nbforiss. It neverasks the provider, so revocation does not take effect until
exppasses.acl.basic.auth.realm.enabledcannot be combined withacl.oidc.enabled;setting both fails startup. Noted on the
IAM page.
acl.oidc.enabled=falsedoes not take the settings out of play entirely:acl.oidc.configuration.urlis still parsed and its scheme still validated.Restructured
needs, and the full list of consistency rules the server enforces at startup.
It also records that the OIDC settings are validated even with
acl.enabledoff, so a bad OIDC config still stops the server from starting.
acl.oidc.cache.ttlmoved there out of"User and group claims", next to the other caching and buffer settings, with
what setting it to
0does in each flow.Cross-page reconciliation
Pages elsewhere in the docs contradicted the above, or predated OIDC entirely:
connect-stringis where the client pages send readers for the credential grammar and listed
only
username,passwordandtoken. It now records that device-flowcredentials are not connect-string keys, and that a static
token=does notrotate.
authentication methods and the
REST API
counted two; both omitted OIDC. The REST API page also now distinguishes an
OIDC bearer token from the REST API token it documents below, since the two
share the
Authorization: Bearerheader.operators to send "an OIDC access token", which under the
groups.encoded.in.token=truethat the Entra ID walkthrough sets is rejectedwith a
401.the device flow's setup steps.
Guide corrections
authorization code request step
that some Identity Providers require the
stateparameter.server.confblock to thePingFederate walkthrough,
which relied on defaults for two settings that have none.
the Entra ID anchor so existing links keep working.
httpand then posted the user's SSO password to whatever URL came back. Bothnon-interactive examples now use
https, and were ported off the legacy ILPSendertoquestdb.connectoverwss, matching the Python client page.endpoint advertises as required by default.
Page restructure
The OIDC and RBAC guides had each grown into a single page too long to navigate
or link into precisely -
oidc.mdxat 1600 lines,rbac.mdat 717. Both arenow directories, one page per topic, with the overview at the section root and
a sidebar category listing the rest.
security/oidc.mdxsecurity/oidc/- overview, how sign-in works, client discovery and tokens, client integration patterns, mapping groups and permissions, PingFederate, Entra IDsecurity/rbac.mdsecurity/rbac/- overview, users and groups, authentication and endpoints, granting permissions, permissions reference, common scenariosOld anchors keep working. Both section roots end with a "where each section
moved" table that holds the old anchor ids and points at the page each section
went to, so external links to
#which-token-to-send,#questdb-configuration-for-pingfederate,#endpoint-permissionsand the reststill land somewhere useful. Every inbound link inside the docs was repointed at
the new canonical anchor rather than left on the redirect.
Two permission statements were corrected while passing through the RBAC pages:
REBASE WALand
SWITCH COLD STORAGE ROLEneed the
SYSTEM ADMINpermission, which is grantable. Both pages described itas unspecified "database administrator (system admin) privileges", and the cold
storage page went further and called the statements "not grantable through RBAC
permissions". Separately,
all_permissions()lists every permission thedatabase supports, not the ones currently applied.
Verification
Every default was read off
EntPropServerConfiguration.java:284-320, notinferred, and the value formats off the getters each one uses. The
startup-failure claims were each checked against the validation in
EntPropServerConfigurationandOidcEndpoint. The token-validation tabledescribes the next Enterprise release, which validates
exp; the earlier"an expired token is still accepted" wording matched the shipped validator and
no longer holds.
nbfandissremain unchecked. The settings endpoint examplematches
SettingsEndpointTest, key for key and in order, includingacl.basic.auth.realm.enabled.The client API surfaces were checked against the client sources, and the
examples compile: Java with
javacagainst the built client, Rust withcargo check, and both C variants and the C++ example with-fsyntax-only -Wall -Wextra. Every type, method, kwarg and C symbol named in these pagesexists and is spelled correctly.
yarn buildpasses with no broken links or anchors.Release coordination
The APIs are right; none of them has shipped. This is the one thing that
should gate the merge:
pom.xmlis1.3.10-SNAPSHOT, newest tag1.3.7questdb-rs7.0.0 has nooidcfeatureinclude/questdb/oidc.habsent frommain5.0.0;CHANGELOG.rstsays5.0.1 (unreleased)4.0.0and3.3.4No client page states a minimum version, and the Rust snippet's
questdb-rs = { version = "7", features = ["oidc"] }is a copy-paste buildfailure against the published crate today. It is not only the OIDC surface:
connect_with_token_provider,questdb_db_connect_exandtoken_provider.rsare all unmerged, so the rotating-provider pattern these sections recommend is
feature-branch work for Rust, C and C++.
Either hold this until the clients release, or add an explicit minimum-version
note to each of the five sections. The Cargo.toml needs fixing either way.
🤖 Generated with Claude Code
https://claude.ai/code/session_0111nSmXn2CmqkYMGobjvxgY