ServerConfig
*di.ServerConfig is the single entry point through which the host injects data, storage, and extension implementations into Wicket. This page lists all of v0.1.4's exported methods (di/server_config.go) grouped by purpose, and explains when each one is read—which determines whether changing it at runtime takes effect.
root, err := di.NewRoot()
if err != nil { /* ... */ }
defer root.Close()
cfg := root.ServerConfig()
// ... a series of Set* calls
cfg.Freeze() // wiring completeEvery SetX on ServerConfig has a corresponding getter X(). The getters are for internal use by the container, and the host generally doesn't need to call them, so they aren't listed individually below.
Set and Provider variants
Most injection seams offer two forms:
SetX(value): pass a value directly. Internally equivalent toSetXProvider(func() T { return value }).SetXProvider(func() T): pass a function for the container to call when it needs the value.
The difference isn't in how you write it, but in when the container calls the function. In v0.1.4 there are three categories:
| Category | Methods involved | When the Provider is called | What it means in practice |
|---|---|---|---|
| Configuration data | Clients, IdentityScopes, ApiResources, ApiScopes, SigningCredentialStores, ValidationKeysStores | Every time the related service is resolved (transient) | The Provider can return the latest data, enabling hot reload without a restart |
| Storage seams | The 10 stores other than the authorization parameters message store | Called once on first use, with the result cached until the process exits | The Provider is just "deferred construction"; calling SetX again later has no effect |
| Authorization parameters message store | AuthorizationParametersMessageStore | Every time it's resolved | Returning nil means no store is used (see below) |
The snippet below has been compiled and run:
// The client list can be hot-reloaded: this function is called every time ClientStore is resolved
var clients atomic.Pointer[[]*models.Client]
initial := []*models.Client{}
clients.Store(&initial)
cfg.SetClientsProvider(func() []*models.Client { return *clients.Load() })
// Storage seam: called once on first use, and the result is cached
cfg.SetSessionStoreProvider(func() session.Store {
return session.NewMemoryStore(wallClock{}) // replace with your persistent adapter
})When hot reloading
Configuration data Providers are called frequently on the request path (ClientStore rebuilds its index and checks for duplicate ids every time it's resolved), so the function itself must be fast and concurrency-safe. Don't query the database inside it; refresh a snapshot in the background and have the Provider only read the snapshot.
Logging
| Method | Description |
|---|---|
SetLogger(*slog.Logger) | Sets the logger used by the container's internal components |
Known issue in v0.1.4: SetLogger has no effect on internal components
The container's internal *slog.Logger is fixed when di.NewRoot() runs. Since ServerConfig is created by NewRoot(), the host can't possibly call SetLogger before that, so internal components always use the default logger: JSON format, Debug level, with source locations, written to standard output. server.WithLogger(...) affects only a small number of the Server's own log entries.
Observed: the Quick start example sets a text-format logger, but internal logs such as endpoint routing and client validation still appear on stdout as JSON. The host currently can't adjust the level of these logs. Some v0.1.4 log entries may contain sensitive request parameters. In production, feed stdout into your logging system, filter by level and remove sensitive fields at the collection side, and avoid sending raw logs to untrusted systems. See Logging and observability.
Clients and resources
| Method | Type | Description |
|---|---|---|
SetClients / SetClientsProvider | []*models.Client | The client registry. Wrapped internally with validation and used as the ClientStore |
SetIdentityScopes / SetIdentityScopesProvider | []*models.IdentityResource | Identity resources (openid, profile, etc.) |
SetApiResources / SetApiResourcesProvider | []*models.ApiResource | API resources; the introspection endpoint uses their names and ApiSecrets to authenticate callers |
SetApiScopes / SetApiScopesProvider | []*models.ApiScope | API scopes |
In v0.1.4, clients and resources can be injected only through these methods; there's no replaceable ClientStore / ResourceStore seam. To load them from a database, use the Provider variants to return a snapshot. For field meanings, see Clients and resources.
Signing and validation keys
| Method | Type | Description |
|---|---|---|
SetSigningCredentialStores / SetSigningCredentialStoresProvider | []storage.SigningCredentialStore | Sources of signing credentials |
SetValidationKeysStores / SetValidationKeysStoresProvider | []storage.ValidationKeysStore | Sources of validation public keys, merged and published to the JWKS |
SetRecordStore / SetRecordStoreProvider | keymgmt.RecordStore | Where key records are persisted when automatic key management is on (opts.KeyManagement.Enabled, on by default) |
Note that the method is named SetRecordStore, not SetKeyRecordStore. For how to choose between the two key sources, see Signing keys.
Storage seams
Method (all have a *Provider variant) | Interface | When not injected and fallback is allowed |
|---|---|---|
SetAuthorizationCodeStore | storage.AuthorizationCodeStore | In-memory implementation + warning log |
SetRefreshTokenStore | storage.RefreshTokenStore | Same as above |
SetReferenceTokenStore | storage.ReferenceTokenStore | Same as above |
SetUserConsentStore | storage.UserConsentStore | Same as above |
SetPersistedGrantStore | storage.PersistedGrantStore | Same as above |
SetDeviceFlowStore | storage.DeviceFlowStore | Same as above |
SetBackchannelAuthenticationRequestStore | storage.BackchannelAuthenticationRequestStore | Same as above |
SetSessionStore | session.Store | Same as above |
SetDistributedCache | services.DistributedCache | Same as above |
SetRecordStore | keymgmt.RecordStore | Same as above |
SetAuthorizationParametersMessageStore | storage.AuthorizationParametersMessageStore | No fallback: no store is used, and authorization parameters go directly into the callback URL's query string |
All 11 items above count toward the completeness check in Freeze(). For interface methods and contracts, see Storage ports.
Releasing resources: root.Close() closes the stores that were actually used, in reverse order of construction; if your adapter implements io.Closer, its Close() is called. Stores that were never used are neither constructed nor closed. The authorization parameters message store is the exception—it isn't wrapped by the container, and the host is responsible for closing it.
Extension points
| Method | Purpose | When not set |
|---|---|---|
SetProfileProvider(func() services.ProfileService) | Provides user profile data: claims in tokens and userinfo (GetProfileData), and whether the user is still active (IsActive). Called on every resolution; returning nil uses the default implementation | The default implementation adds no claims at all, and IsActive is always true. You must inject one if you need user claims such as name and email |
SetResourceOwnerValidator(validators.ResourceOwnerPasswordValidator) | Enables the password grant and validates username and password | Always rejects (NotSupportedResourceOwnerPasswordValidator), and the discovery document doesn't advertise password |
SetBackchannelAuthenticationUserValidator(validators.BackchannelAuthenticationUserValidator) | Locates the user in a CIBA request from login_hint and the like | The default implementation is for development and integration testing only and doesn't parse or verify user hints—in production you must inject your own implementation |
SetAuthenticationHandlerProvider(scheme, func(godi.Provider) auth.Handler) | Provides a handler factory for an authentication scheme name, overriding the built-in registration | Uses the built-in handler; see Identity integration overview |
SetExternalFailurePath(string) | Redirect path when external login fails | Empty string (no redirect) |
SetBackChannelLogoutHttpClient(*http.Client) | HTTP client used for backchannel logout notifications | nil |
SetBackchannelAuthenticationNotificationHttpClient(*http.Client) | HTTP client used for CIBA notifications | http.DefaultClient (no timeout) |
Backchannel logout notifications aren't sent in v0.1.4
BackchannelLogoutService.SendLogoutNotificationsAsync, which is responsible for sending notifications, is currently a no-op that returns nil directly, so SetBackChannelLogoutHttpClient has no observable effect for now—even though the discovery document declares backchannel_logout_supported: true. RPs that depend on backchannel logout need to handle it some other way; see Sessions and logout.
The CIBA notification client defaults to http.DefaultClient, which has no timeout; inject an *http.Client with a timeout set.
Lifecycle
| Method | Description |
|---|---|
AllowInMemoryFallback() | Declares that "stores that aren't injected may fall back to in-memory implementations". For local development and testing only |
Freeze() | Marks wiring as complete. If AllowInMemoryFallback() hasn't been called, checks each of the 11 storage seams in the table above, and panics if any isn't injected |
Frozen() bool | Whether Freeze() has been called |
Observed panic message when AllowInMemoryFallback() isn't called:
panic: store seam "authorization parameters message store" not injected by host and in-memory fallback not declaredA few notes:
- Production wiring should not call
AllowInMemoryFallback(), so missing injections surface at startup. In a multi-replica deployment, any store left in process memory causes cross-node requests to fail at random. - The frozen check is also performed again when each store is first used; but if
Freeze()was never called, that check doesn't fire, and stores that weren't injected silently fall back to memory. So always callFreeze(). Freeze()doesn't lock theSet*methods—calling them afterwards doesn't raise an error, but once a storage seam has been used it isn't read again. Do all injection beforeFreeze()and before you start serving.