Configuration
For developers and operators tuning a deployment. This page is the single reference for the cosec.* properties the starter binds, and for the cocache.* properties that change how CoSec behaves; other pages name a property and link here for its type and default.
Every property is optional unless marked required. A property only acts when the module it configures is on the classpath: each integration is a module the application adds next to the starter (Modules). Durations take Spring's format (500ms, 10s, 7d), data sizes 16MB; sets and lists take a comma-separated value or a YAML list; enum values are case-insensitive. Properties that 7.x had and 8.0 does not are listed in Upgrading.
Unknown properties
Startup fails on a cosec.* key CoSec does not read, whatever else is configured (cosec.enabled=false included): a misspelled, renamed or removed key would otherwise do nothing. A key is known when the configuration metadata on the classpath (META-INF/spring-configuration-metadata.json) declares it, or when it lies under a map- or list-valued property. The message lists every unknown key and links the table of removed and renamed keys. No property turns this check off: a key prefix a release replaced, had it been ignored, would leave token revocations where nothing reads them.
Keys are compared as Spring's binder compares them, so every spelling it documents for a known key is known: local-max-age, localMaxAge or local_max_age in a properties file, and COSEC_AUTHORIZATION_SNAPSHOT_LOCALMAXAGE as an environment variable (upper case, dots as underscores, dashes dropped). A variable that is not that spelling of a key CoSec reads stays unknown, a typo (COSEC_PIPELINE_TIMEUOT) and the legacy COSEC_AUTHORIZATION_SNAPSHOT_LOCAL_MAX_AGE alike: the message names it as cosec.authorization.snapshot.local.max.age.
One thing is never an unknown key: the variables Kubernetes injects for a Service named cosec or cosec-* (service links), which Spring reads as cosec.* keys (COSEC_GATEWAY_SERVICE_HOST as cosec.gateway.service.host). The check skips exactly their shapes, matched on the variable names and only in the process environment, where <SVC> is COSEC or COSEC_<NAME>:
<SVC>_SERVICE_HOST,<SVC>_SERVICE_PORTand<SVC>_SERVICE_PORT_<PORT NAME>;<SVC>_PORT, when the value is atcp://,udp://orsctp://URL;<SVC>_PORT_<port>_<TCP|UDP|SCTP>and the same with_PROTO,_PORTor_ADDR.
Every other COSEC_* variable is checked (COSEC_JWT_SECRT fails startup), and so is the same key in application.yaml, a system property or any other source. No CoSec property has one of these names, and a known key is never unknown whatever its source, so the exemption cannot hide a property CoSec reads. Setting enableServiceLinks: false on the pod keeps the variables out of the environment altogether (Troubleshooting).
Feature switches
| Property | Type | Default | Turns on |
|---|---|---|---|
cosec.enabled | boolean | true | every CoSec auto-configuration |
cosec.authentication.login.enabled | boolean | true | the login beans, which turn credentials into a token: CompositeAuthentication over your Authentication beans, TokenCompositeAuthentication with JWT, and social login. Authenticating requests in the pipeline does not depend on it |
cosec.authentication.login.social.enabled | boolean | true | social login (cosec-social; needs cosec.authentication.login.enabled) |
cosec.jwt.enabled | boolean | true; false with cosec.pipeline.mode=gateway | issuing, verifying and revoking JWTs. Off, the pipeline rejects every token (token-invalid) and treats a request without one as anonymous |
cosec.api-key.enabled | boolean | false | API keys for machine clients. true or false only: any other value fails startup. |
cosec.audit.enabled | boolean | true | the audit log listener, and the Kafka audit listener with cosec.audit.kafka.enabled |
cosec.audit.kafka.enabled | boolean | true | the Kafka audit listener (cosec-kafka and a KafkaOperations bean) |
cosec.metrics.enabled | boolean | true | the metrics listener (cosec-micrometer and a MeterRegistry bean, Spring Boot's own auto-configured one included) |
cosec.ip2region.enabled | boolean | true | the ext.ip2region.* attribute provider (cosec-ip2region) |
cosec.openapi.enabled | boolean | true | the bearer-auth customizer of the OpenAPI document (cosec-openapi) |
cosec.authorization.shadow.enabled | boolean | true | this instance's part in shadow windows (cosec-cocache); see Shadow windows |
cosec.gateway.identity.enabled | boolean | true | on a Spring Cloud Gateway host, forwarding the verified identity to the services behind it; see Gateway identity |
These have no switch:
- Tracing is on whenever
cosec-opentelemetryis on the classpath (Tracing). - The control plane is on whenever
cosec-controlandcosec-cocacheare on the classpath; its routes need a WebFlux or WebMvc application (Control plane). - The rate limiter is always there: on Redis when a
ReactiveStringRedisTemplatebean exists (cosec-cocache), otherwise counted per instance (Rate limiting). - Token revocation is always on with JWT (Startup).
- The
cosecServiceGeneratoractuator endpoint is there whenevercosec-openapi, springdoc and Spring Boot Actuator are; expose it like any actuator endpoint.
Key prefix
Every key a deployment writes to Redis lives under one prefix (SNP-9): snapshot documents, control-plane records, shadow reports, revocations, memberships, API keys, rate limit counters and OAuth states (Redis keys). Two deployments on one Redis that use different prefixes share nothing. Within one application, CoCache refuses to create a cache whose storage keys would overlap another's, so a prefix that puts a CoSec cache under one of the application's own caches fails startup (Troubleshooting). It replaced cosec.authorization.snapshot.key-prefix, cosec.authorization.cache.key-prefix and cosec.api-key.key-prefix: an environment that sets one of them fails startup, as any key CoSec does not read does (Troubleshooting).
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.key-prefix | string | cosec | The prefix of every Redis key, with a trailing : removed. Every service of one deployment, its control plane and cosec-migrate --key-prefix use the same value. With the Redis rate limiter, a prefix whose first { is immediately followed by } (an empty hash tag) fails startup, and one with a hash tag of its own, such as {cosec}, puts every rate limit counter on one Redis Cluster slot (Rate limiting). |
Pipeline
cosec.pipeline.*: how one request is read and bounded.
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.pipeline.timeout | duration | 5s | The whole evaluation of one request. Past it the request is answered 503 unavailable. |
cosec.pipeline.authentication-timeout | duration | 1s | One call of the Authenticator. Past it, 503. |
cosec.pipeline.allow-query-token | boolean | false | Also read the access token from the authorization query parameter. Off because URLs end up in access logs, browser history and Referer headers. |
cosec.pipeline.allow-cookie-token | boolean | false | Also read the access token from the authorization cookie. Off because a browser sends a cookie on its own, on a cross-site request too: turn it on only with your own CSRF defense (Authentication). |
cosec.pipeline.api-key-header | string | X-API-Key | The request header an API key comes in. No document may read it: every compiler of the host refuses a reference to it, as to the other credential headers (Headers). |
cosec.pipeline.trace-level | off, full, exhaustive | off | How much of each evaluation the engine traces. Decision events carry the trace as evaluationTrace (null at off, and when the request did not reach the engine); a higher level costs time on every request. |
cosec.pipeline.mode | enforce, gateway | enforce | enforce: the host decides with policies. gateway: a service behind a CoSec gateway reads the caller from the CoSec-Identity header the gateway forwards, and wires no authenticator, API keys, snapshot, policies, control plane or Redis of its own (Services behind the gateway). Any other value fails startup, and so does gateway on a Spring Cloud Gateway host. In gateway mode only the gateway may be able to reach the service: startup logs a WARN saying so. In gateway mode cosec.jwt.enabled defaults to false: a service that also issues tokens sets it to true. |
cosec.pipeline.identity.max-age | duration | 60s | In gateway mode, an identity issued longer ago than this, plus cosec.authentication.revocation.skew, is refused: 401 token-expired. |
cosec.pipeline.identity.max-size | data size | 8KB | In gateway mode, a larger CoSec-Identity header is refused: 400 invalid-request. |
Authorization
cosec.authorization.*: the host's scope and the per-stage timeouts.
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.authorization.scope.app-id | string | none | The app this host serves. A credential that declares another app, or none, is rejected (403 scope-rejected). Unset, the credential's app applies (Apps). On a gateway, a route's metadata cosec-app-id takes precedence for that route's requests (Gateway). |
cosec.authorization.remote-ip.max-trusted-index | int | 1 | How many proxy hops of X-Forwarded-For to trust when resolving the client IP. 0 ignores the header (the host is exposed directly); N takes the entry the N-th closest proxy appended. |
cosec.authorization.membership.timeout | duration | 500ms | One call of the MembershipProvider. Past it, or when it fails, 503. |
cosec.authorization.attributes.timeout | duration | 300ms | One call of an AttributeProvider that sets no timeout of its own. Past it, its attributes are unavailable, and a statement that needs them makes the decision Indeterminate. |
cosec.authorization.routes.location | comma-separated resource locations | unset | The route maps a WebFlux or WebMvc host loads at startup, each logged with its service and route count. A location that does not exist, a map that does not parse, or a map with no routes fails startup. Unset, no route map is loaded and every request is authorized in the http namespace, unless classpath:cosec-routes.json exists: startup then fails until the property names it or is set empty. Empty runs without a route map on purpose. A Spring Cloud Gateway host ignores it and loads no route map. |
Gateway
cosec.gateway.*, on a Spring Cloud Gateway host (cosec-gateway) that decides with policies.
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.gateway.unprotected-endpoints | set of endpoint ids | empty | Actuator endpoints that may be exposed over HTTP on the gateway's application port, where CoSec does not authorize them, because something else protects them. Any other one but health fails startup there (STA-2); gateway, which changes routes, is never accepted. On a separate management.server.port any endpoint may be exposed (Actuator on a gateway). |
Gateway identity
cosec.gateway.identity.*, on a Spring Cloud Gateway host (cosec-gateway): what the gateway forwards to the services behind it. On a permit with a principal it sets CoSec-Identity to the identity it verified (format: schema/cosec-identity.schema.json), on a route that receives it: a route to a registered service (lb://), or one with the metadata cosec-identity: true. A route with cosec-identity: false, or to any other URI without the metadata, receives no identity (Forwarding the identity). The gateway always removes a CoSec-Identity the client sent, whatever these properties, the mode or cosec.enabled say.
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.gateway.identity.enabled | boolean | true | Forward the verified identity to the routes that receive it. Off, the gateway still strips the client's. |
cosec.gateway.identity.claims | set of attribute names | empty | Principal attributes forwarded with the identity as claims, such as name and email (for a JWT, keys of its attributes claim, not top-level claims): strings of at most 1024 characters, numbers and booleans. Any other value fails the request (500). |
cosec.gateway.identity.max-size | data size | 8KB | A larger encoded identity fails the request closed (500) rather than being cut. |
cosec.gateway.identity.forward-authorization | boolean | false | Whether the client's credentials still go downstream: the Authorization header, the API key header (cosec.pipeline.api-key-header), the authorization query parameter and the authorization cookie. Off, the gateway removes them from every request it forwards (the cookie from each Cookie header, other cookies kept): a service behind it needs none. On, they go only to the routes that would receive the identity (lb://, or cosec-identity: true), whether or not cosec.gateway.identity.enabled is on. Turn it on only for services that verify the token again or call other APIs on the user's behalf. |
A credential that never expires (an API key without expires-at) cannot be forwarded: its permits are answered 500.
Rate limiting
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.authorization.rate-limit.timeout | duration | 500ms | One call of the RateLimiter. Past it, the rule's strictFailure decides: 503, or the rule is skipped. |
Rate limit rules are part of the documents (Rate limiting). The limiter is ReactiveRedisRateLimiter when a ReactiveStringRedisTemplate bean exists, otherwise LocalRateLimiter, which counts per instance. The Redis counters have a fixed key prefix (Redis keys). A RateLimiter bean of your own replaces both.
Snapshot
cosec.authorization.snapshot.*: how an instance reads and holds the platform, app and tenant documents (How instances read documents).
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.authorization.snapshot.local-max-age | duration | 30s | How long CoCache's local copy of a document may serve a cold load before Redis is asked again. It applies whatever a cache customizer or cocache.* property sets. |
cosec.authorization.snapshot.revalidate-interval | duration | 10s | How often a pass starts that confirms every installed document against Redis with a conditional read; while a pass is still in flight, the next starts as soon as it ends, never overlapped. A change whose invalidation message was lost is installed by the first pass that reaches its document after the change is readable (SNP-6). At least 1s, at most 5m, less than lag-warn and, with fail-closed.tenants, less than fail-closed.max-lag; anything else fails startup. Each pass costs Redis a few microseconds per document, whatever its size (Storage). |
cosec.authorization.snapshot.lag-warn | duration | 60s | A document not confirmed for longer is logged at WARN. Must not exceed lag-escalate. |
cosec.authorization.snapshot.lag-escalate | duration | 5m | A document not confirmed for longer is logged at ERROR as stale. |
cosec.authorization.snapshot.fail-closed.tenants | set of tenant ids | empty | Tenants that must not be decided on old documents: when the oldest document a request of such a tenant reads was confirmed longer ago than max-lag, the request is 503 (SourceUnavailable) unless a DENY matches. Other tenants keep deciding on what is installed. |
cosec.authorization.snapshot.fail-closed.max-lag | duration | 30m | The lag beyond which the tenants above fail closed. |
cosec.authorization.snapshot.cold-load-timeout | duration | 2s | How long a request waits for a document that is not installed. Past it the document counts as failed for that request. |
cosec.authorization.snapshot.max-apps | int | 1024 | The most app documents held in memory; the least recently used one is dropped beyond it, and loaded again when needed. |
cosec.authorization.snapshot.max-tenants | int | 1024 | The same for tenant documents. The platform document is never dropped. |
cosec.authorization.snapshot.prewarm-apps | list of app ids | empty | App documents loaded at startup, after the platform document, which is always loaded. Readiness refuses traffic until the platform document and every listed document are installed; liveness is not affected. |
cosec.authorization.snapshot.prewarm-tenants | list of tenant ids | empty | Tenant documents loaded at startup, as above. |
cosec.authorization.snapshot.compile-threads | int | 2 | Threads that compile documents. |
cosec.authorization.snapshot.eviction-check | fail, warn | fail | What startup does when Redis' maxmemory-policy is not noeviction on every node, or cannot be read (a failed or denied INFO memory, a wrapped or custom CoCache backend). fail stops startup. warn starts and logs a WARN that begins with !!! (Redis requirements). The in-memory CoCache backend always passes. Either way the instance reads the policy again every revalidate-interval, and the cosecSnapshotEviction health indicator is DOWN while Redis may evict; a policy it cannot read is DOWN under fail and UP with eviction: unknown under warn (Troubleshooting). |
Shadow windows
cosec.authorization.shadow.*: the data plane's part in the control plane's shadow windows. It needs cosec-cocache. How windows work: Testing changes.
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.authorization.shadow.enabled | boolean | true | Evaluate sampled requests again on the staged candidates, and write this instance's reports and status to Redis. Off, this instance's traffic counts for nothing in a window, a candidate it cannot compile does not block one, and it writes no status for the propagation view. |
cosec.authorization.shadow.sample-rate | double, 0 to 1 | 0.1 | Share of requests evaluated again on the candidates, off the request path. |
cosec.authorization.shadow.report-interval | duration | 10s | How often the instance writes its reports and its status. |
cosec.authorization.shadow.instance-id | string | a random UUID | This instance's id in reports and statuses. Unique per running instance; only letters, digits, ., _ and -, since it ends a Redis key. Any other id fails startup. |
Authentication
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.authentication.revocation.skew | duration | 5s | How much later than a subject revocation a token may have been issued and still be revoked by it: room for an issuer whose clock runs ahead. |
JWT
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.jwt.secret | string | none | Required while JWT is enabled; startup fails when it is blank or shorter than the algorithm needs: 32 bytes (UTF-8) for hmac256, 48 for hmac384, 64 for hmac512 (RFC 7518 §3.2). Use a long random value from a secret store. |
cosec.jwt.algorithm | hmac256, hmac384, hmac512 | hmac256 | Signing algorithm. Issuers and verifiers must agree. |
cosec.jwt.token-validity.access | duration | 10m | Access token lifetime. |
cosec.jwt.token-validity.refresh | duration | 7d | Refresh token lifetime; a revocation is kept in Redis this long, plus one second. |
With JWT enabled, startup fails unless revocations can be recorded and read: cosec-cocache, or a TokenStore and a RevocationReader bean of your own.
API keys
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.api-key.store | memory, redis | memory | memory: the keys configured below. redis: keys shared by every instance, under <cosec.key-prefix>:api-key; needs cosec-cocache, a StringRedisTemplate and a ReactiveStringRedisTemplate. |
cosec.api-key.keys[n].* | list | empty | Keys of the memory store; ignored with redis. |
The header is cosec.pipeline.api-key-header. API keys need a MembershipProvider bean, or the application does not start (IDN-6, API keys). Each entry of cosec.api-key.keys, where any other field fails startup:
| Field | Type | Default | Meaning |
|---|---|---|---|
id | string | none | Required. The key's id; ids must be unique. |
hash | string | none | Required. SHA-256 of the key, 64 lowercase hex characters. Never configure the key itself. |
principal-id | string | none | Required. The principal the key authenticates as. |
tenant-id | string | none | Required. Tenant of the principal. |
app | string | none | The app the key belongs to (Apps). Left out, it declares none, and a host with an app refuses it. |
roles | set | none | The cap: the key holds only these of its principal's roles (role, role@space), and no attached policy or principal binding; [] leaves only what Anyone bindings give. Set this or uncapped. |
uncapped | boolean | false | true for a key that holds everything its principal holds. Exactly one of roles and uncapped: true is set, or the application does not start. |
expires-at | instant | none | After this instant the key is rejected. |
enabled | boolean | true | A disabled key is rejected. |
Cache
cosec.authentication.revocation.cache.*: the revoked token and revoked subject caches, created with JWT and cosec-cocache. Snapshot documents have their own properties (Snapshot).
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.authentication.revocation.cache.* | local cache settings | local-max-age: 30s, maximum-size: 100000 | Local copy of revocation lookups (RevokedTokenCache and RevokedSubjectCache). |
Local cache settings
| Property | Type | Default | Effect |
|---|---|---|---|
maximum-size | long | 100000 | Maximum entries in the local copy. |
initial-capacity | int | CoCache's | Initial capacity of the local copy. |
local-max-age | duration (a bare number is seconds) | 30s | How long a local copy lives at most: the bound on staleness when an invalidation message is lost. |
expire-after-access | duration (a bare number is seconds) | none | Drop a local copy not read for this long. |
These size and age the local copy only. A revocation's lifetime in Redis is set when it is written.
Audit
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.audit.kafka.topic | string | cosec-audit | Kafka topic of the audit events. |
cosec.audit.kafka.delivery | best-effort, required | best-effort | How the Kafka audit listener is delivered. required waits for the broker's acknowledgement, so a permit that cannot be audited is a 503; configure the producer with acks=all. Startup fails when it is required without cosec-kafka (STA-1). |
Decision listeners
cosec.observability.listener.*: the time a REQUIRED decision listener has, and the queues of the BEST_EFFORT ones (Decision listeners).
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.observability.listener.required-timeout | duration | 1s | How long one REQUIRED decision listener may take. A failure or timeout turns a permit into 503. |
cosec.observability.listener.queue-capacity | int | 1024 | Events one BEST_EFFORT listener may have waiting. A full queue drops the next event and counts it (cosec.listener.dropped). |
cosec.observability.listener.drain-timeout | duration | 5s | On shutdown, how long queued events still go out; the rest is then dropped. The Kafka audit listener gets as long again for the sends it still holds. |
Control plane
cosec.control.*, with cosec-control and cosec-cocache (Publishing documents).
The control plane checks every approver for revocation as the pipeline reads it: startup fails without a RevocationReader bean or CoSec's tokens with cosec-cocache. Declare RevocationReader.NONE only when no approver's credential is ever revoked.
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.control.app-id | string | cosec-control | The app control-plane actions are authorized in. |
cosec.control.http.base-path | string | /cosec/control | Where the control API is served. Its routes are under {base-path}/v1, so moving the API keeps the version. |
cosec.control.http.max-body-size | data size | 16MB | The largest request body the control API reads on a WebMvc host. A larger body is a 400 invalid-request, unread. On WebFlux, spring.codec.max-in-memory-size (256 KB by default) bounds the body instead. On either stack, a request without a permitted principal has no body read at all (401). |
cosec.control.revisions.max-count | int | 50 | Revisions always kept per document; older ones are reclaimed once also older than min-age. Revisions an open change rests on are kept. |
cosec.control.revisions.min-age | duration | 7d | Revisions younger than this are always kept. |
cosec.control.revisions.orphan-age | duration | 1h | Records of conflicted publishes are removed once this old. |
cosec.control.revisions.reclaim-interval | duration | 1h | How often each control-plane instance reclaims revisions; a lease lets one instance at a time do each document. Must be positive. |
cosec.control.changes.expire-after | duration | 7d | An open change request expires this long after it was proposed, and leaves the list of open changes. |
cosec.control.approval.expire-after | duration | 72h | How long an approval stays valid. Startup fails when it is longer than cosec.jwt.token-validity.refresh, how long a token revocation is kept: an approval would outlive the revocation of its token. |
cosec.control.approval.four-eyes | boolean | false | Whether the changes of a platform principal holding cosec:approve on the document (the trust root) are analyzed too: what the analysis cannot prove contained then needs another platform principal's approval. A change that escalates needs one either way. |
cosec.control.break-glass.max-duration | duration | 4h | The longest a break-glass binding may last. More than 24h fails startup. |
cosec.control.shadow.min-duration | duration | 15m | A shadow window closes no earlier than this, once it also has min-samples. |
cosec.control.shadow.min-samples | long | 1000 | Compared samples a window needs to close before max-duration. |
cosec.control.shadow.max-duration | duration | 24h | A window closes at this age with fewer samples, as insufficient evidence the publisher must acknowledge. Must not be less than min-duration. |
cosec.control.shadow.check-interval | duration | 1m | How often shadowing changes are looked at, by one instance at a time; a window closes up to this late. Must be positive. |
cosec.control.audit.required-timeout | duration | 5s | How long every REQUIRED change listener together may take to record a gated step (publish, break-glass) before the step fails with 503. |
cosec.control.audit.kafka.enabled | boolean | true | The Kafka change listener (cosec-kafka and a KafkaOperations bean). |
cosec.control.audit.kafka.topic | string | cosec-control-audit | Kafka topic of the change events. |
cosec.control.audit.kafka.delivery | best-effort, required | best-effort | required makes publishing and break-glass wait for the broker. Startup fails when it is required and no Kafka change listener could be created. |
Ids
cosec.cosid.*, with cosec-cosid and an application on CosId 5 (Ids).
| Property | Type | Default | Effect |
|---|---|---|---|
cosec.cosid.generator | string | default | The CosId generator whose ids CoSec takes, with its codec: request, decision event, token and change ids. A generator whose ids are not 1 to 64 characters of A-Z a-z 0-9 . _ - fails startup. Set without cosec-cosid, startup fails and names it (Troubleshooting). |
Social login
cosec.authentication.login.social.registration.<name>.* configures one OAuth provider per <name>:
cosec:
authentication:
login:
social:
registration:
github:
type: github # a JustAuth AuthDefaultSource
client-id: ${GITHUB_CLIENT_ID}
client-secret: ${GITHUB_CLIENT_SECRET}
redirect-uri: https://app.example.com/oauth/github/callbacktype (a JustAuth AuthDefaultSource) and client-id are required. The other fields map one-to-one to JustAuth's AuthConfig: client-secret, redirect-uri, alipay-public-key, union-id, stack-overflow-key, agent-id, usertype, domain-prefix, http-config, ignore-check-state, scopes, device-id, client-os-type, pack-id, pkce, auth-server-id, ignore-check-redirect-uri, tenant-id, kid, team-id, login-type (default CorpApp), lang (default zh), ding-talk-org-type, ding-talk-corp-id, ding-talk-exclusive-login and ding-talk-exclusive-corp-id. Booleans default to false, the others to none. Social login keeps its OAuth state in Redis through a StringRedisTemplate.
CoCache properties that affect CoSec
With cosec-cocache, CoSec creates its caches through CoCache 8's CoCacheFactory, so CoCache's configuration layers reach them like any other cache. The full list of CoCache properties is in CoCache's documentation. Every CoCache duration needs a unit (2s, 10m); a bare number fails startup.
| Property | Effect on CoSec |
|---|---|
cocache.backend | redis (default) or memory. With memory nothing is shared between instances, the eviction checks pass, and every instance starts with no documents: local development and tests only. |
cocache.defaults.cache.*, cocache.defaults.store.* | Fill only what a cache's declaration leaves unwritten. CoSec's caches declare what they depend on, so these change little: MembershipCache is a cache, every other CoSec cache a store (Precedence). |
cocache.overrides.cache.*, cocache.overrides.store.* | Tighten staleness bounds (local-max-age, absent-ttl) and read-failure over every cache, except for the fields CoSec locks. |
cocache.caches.[<CacheName>].* | The policy of one cache, by the names above. Setting a field CoSec locks fails startup (Precedence). So does a key CoCache does not know or that does not apply to that cache, usually a typo, unless cocache.caches-validation: warn (Troubleshooting). CoSec publishes the name of every cache it may create, so an entry for one this instance does not create (a control-plane cache on a service without the control plane, MembershipCache without a MembershipLoader, a revocation table with JWT off) starts. |
cocache.key-prefix | None: CoSec locks its own prefix, cosec.key-prefix. |
cocache.redis.command-timeout | How long one Redis command may take (2s by default). A snapshot read that times out is a failed read (When Redis is unavailable). |
cocache.redis.channel-mode | pubsub or sharded invalidation. Every instance, and the control plane, must agree. |
cocache.redis.assume-maxmemory-policy | The policy CoCache's own eviction check assumes when INFO memory cannot be read. CoCache refuses to start with allkeys-*, or with an unreadable policy and this unset, because CoSec's documents and records are Stores; cosec.authorization.snapshot.eviction-check=warn does not change that (Redis requirements). |
cocache.redis.store-eviction-check | What CoCache does with a volatile-* policy and a Store whose policy has a finite ttl (the shadow caches): warn (default), fail or off. It counts the revocation tables too, whose entries carry a lifetime of their own, but only once the instance has written or read one, so not at startup; CoSec's own check refuses every policy but noeviction from the start (Redis requirements). |
cocache.redis.store-eviction-check-interval | How often CoCache reads the policy again, for its log and its storeEviction health detail. CoSec reads it again every revalidate-interval on its own, for cosecSnapshotEviction. |
spring.data.redis.* | The Redis CoCache connects to, and the one the rate limiter and the Redis API key store use. |
The control plane's caches and the shadow caches are created with the same locks as the snapshot store, so the same configuration rules apply to them.
Precedence
Every CoSec cache is assembled by CoCache's layers, field by field; a later step wins over an earlier one:
- CoCache's type default (
store: nottl,local-max-age30 s;cache:ttl1 h); cocache.defaults.cache.*orcocache.defaults.store.*, for the fields CoSec's declaration leaves unwritten;- CoSec's declaration:
cosec.authorization.snapshot.local-max-agefor the snapshot store,cosec.authentication.revocation.cache.*andunknown-read: absentfor the revocation tables (a failed read answers "not revoked";unknown-read: throwin step 6 makes the revocation check fail closed with503instead); - the application's CoCache
CacheSpecCustomizerbeans; cocache.overrides.*, which only tighten;cocache.caches.[<CacheName>].*;- CoSec's constraints, applied last, after
unlocktoo.
A value CoSec declares wins over cocache.defaults.* whatever it is, one equal to CoCache's own default (a maximum-size of 10000) included. So a cocache.caches.[<CacheName>].* property wins over the matching cosec.authentication.revocation.cache.* one. CoSec locks some fields (CoCache's lock): the global layers skip them, a cocache.caches entry that sets one fails startup (unless the entry lists it in unlock, CoCache's break glass), and a customizer that changes one throws. Some are also constrained (step 7), so even unlock cannot change them:
| Cache | Locked | Constrained | Why |
|---|---|---|---|
PolicySnapshotCache and the control plane's and shadow window's stores of record (RevisionRecordCache, RevisionIndexCache, ReferenceIndexCache, ControlLeaseCache, ControlListCache, ControlAppListCache, ChangeRequestCache, StageCache, CandidateCache) | ttl, unknown-read; the snapshot store also local-max-age | ttl never expires, unknown-read: throw | Redis is the store of record (Storage): an expiry or a failed read taken for "absent" would silently drop a document, and with it its DENY statements. As CoCache Stores, they never reclaim content they cannot read, and a failed delete throws instead of being retried later, when it could delete a newer write. |
ShadowReportCache, ShadowReporterCache, InstanceStatusCache, InstanceIndexCache | unknown-read, ttl | unknown-read: throw; ttl the declared 8 days (reports, reporters) or 2 minutes (statuses; the list of instances keeps the 8 days) | An outage must not read as "no samples" or "no instance", and an instance's samples must not expire from a window it reported in. |
MembershipCache | read-failure, unknown-read | ttl finite (one minute when forever), ttl-jitter: 0s, read-failure and unknown-read: throw | A failed read must never become "no memberships", and a lost invalidation must expire. |
RevokedTokenCache, RevokedSubjectCache | read-timeout | read-timeout: the store's command timeout | As Stores they never reclaim an unreadable revocation, which would make its tokens valid again. A read that times out reads "not revoked" (unknown-read: absent), so no layer, cocache.defaults.store.read-timeout included, may time it out sooner. |
| every CoSec cache | key prefix, namespace, key encoder, codec, key filter, ttl function, data source | — | The storage layout is CoSec's, where every instance and the control plane look for it. |
Next: where the documents live and how instances read them, in Storage and caching.