Settings are resolved (lowest to highest precedence) from:
built-in defaults,
lws.properties on the classpath,
./lws.properties in the working directory,
-Dlws.* JVM system properties.
A starter file with every key and inline guidance ships as
lws.example.properties —
copy it to lws.properties and edit. The tables below cover every settings group; the
README’s configuration table and the example
file are the complete per-key reference.
Fail-fast validation
Invalid values fail fast at startup with an actionable message naming the key, the value, and
what was expected — for example an out-of-range port, an unknown enum (the message lists the allowed
values), a non-true/false flag, or a malformed lws.base-uri — rather than a raw parse exception.
Booleans are strict (true/false only). Unsafe combinations are refused too: no lws.owners
without lws.dev.open=true, lws.dev.open together with lws.require-https, and
lws.cors.allowed-origins=* in open mode. The bare-Jetty launcher prints the message and exits
without a stack trace.
Core
Property
Default
Meaning
lws.base-uri
http://localhost:8080
Public base IRI of the storage. Must be an http(s) URL; it may carry a path (https://host/lws) when a proxy publishes the server under one — see Deployment. In production set it to the external https:// URL — every minted IRI, DPoP htu, WebID, ACL and the authorization server’s issuer derive from it.
lws.listen-port
port of lws.base-uri, else 8080
HTTP listen port, when it differs from the base URI’s — typically behind a reverse proxy, where the base URI is the external address.
lws.data-dir
lws-data
Directory for the TDB2 dataset, binary blobs and signing keys (key directories are made owner-only at startup).
lws.system-prefix
.lws
Path prefix for the server’s own endpoints (storage description, token endpoint, JWKS, subscriptions, search, access requests/grants). Reserved: no resource may be created under it.
lws.owners
(empty)
Space/comma-separated owner WebIDs/DIDs. Empty ⇒ the server refuses to start unless lws.dev.open=true.
lws.dev.open
false
Permit the development-only postures: an empty lws.owners (open mode — everything permitted for everyone) and lws.ui.dev-login on a non-loopback base URI.
lws.public-read
false
Whether non-owners may read the storage at all — storage-wide, not per resource. To open one resource or subtree, issue an access grant to foaf:Agent instead.
lws.access-control
OWNER
OWNER (single-tenant owner/public-read) or WAC (multi-user Web Access Control).
WAC: reuse of resolved acl:agentGroup documents, retry delay for unreachable ones, and how many one decision may fetch.
lws.mask-forbidden-as-not-found
true
Answer an authenticated principal without Read 404 whether or not the resource exists (anonymous clients still get 401).
RDF backend
Property
Default
Meaning
lws.sparql.mode
TDB2
TDB2 (embedded) or REMOTE (any SPARQL 1.1 service — experimental).
lws.sparql.query / .update / .gsp
Required when mode=REMOTE: Query / Update / Graph Store Protocol endpoints, absolute http(s) URLs.
lws.sparql.remote.accept-no-transactions
false
Must be true to run REMOTE. Remote SPARQL has no transactions, so conditional writes, atomic deletes (with the ACL and linkset), the quota and Slug uniqueness become best-effort.
Authorization server & access tokens
The storage embeds an OAuth 2.0 authorization server, per the LWS core Authorization section. See
Authentication.
Property
Default
Meaning
lws.oauth.enabled
true
Run the embedded authorization server: Token Exchange (RFC 8693) at <system-prefix>/token, RFC 8414 metadata at /.well-known/lws-configuration, signing key at <system-prefix>/jwks. Its issuer is lws.base-uri.
lws.oauth.access-token-lifetime-seconds
300
Lifetime of the RFC 9068 access tokens it issues (1–3600), never longer than the exchanged credential.
lws.oauth.trusted-issuers
External authorization servers whose RFC 9068 access tokens are also accepted (metadata read from their /.well-known/lws-configuration).
lws.oauth.accept-authentication-credentials
true
Also accept an authentication credential presented directly as the Authorization token (the pre-baseline behaviour).
Authentication
Property
Default
Meaning
lws.audience
this storage’s IRI (lws.base-uri, with and without a trailing /)
Accepted aud values for JWT credentials (comma/space separated). A credential naming another audience is always refused.
lws.audience.require
true
Refuse a JWT credential with no aud at all.
lws.token.max-lifetime-seconds
3600
Max lifetime of a self-signed (SSI-CID, including did:key) credential (0 = unlimited).
Inbox notified when a new access request is created.
Browser access (CORS)
Property
Default
Meaning
lws.cors.allowed-origins
(empty)
Web origins allowed to call the API from a browser. Empty means no cross-origin access — no Access-Control-* header is sent. * is allowed (credentials are never sent) but refused in open mode. /app and /callback are never CORS-enabled.
lws.cors.max-age-seconds
600
How long a browser may cache a preflight.
Limits & integrity
Property
Default
Meaning
lws.quota.max-bytes
0
Max total binary-content bytes (0 = unlimited); over-quota writes get 507.
lws.max-request-bytes
67108864
Max request-body size (0 = unlimited); larger requests get 413.
lws.linkset.max-bytes
1048576
Max serialized size of one resource’s user-managed linkset (.meta); a write whose result would exceed it gets 400.
SSRF guards
Property
Default
Meaning
lws.sparql-update.allowed-hosts
Hosts a SPARQL Update LOAD/SERVICE may fetch (empty = blocked entirely).
lws.fetch.block-private-addresses
true
Block auth/WAC dereferences to private/loopback/link-local (incl. cloud-metadata) addresses.
lws.fetch.allowed-hosts
Hosts exempt from that block (e.g. an internal IdP).
lws.webhook.block-private-addresses
true
Block notification delivery to private/loopback/metadata inbox addresses.
lws.webhook.allowed-hosts
Inbox hosts exempt from that block.
lws.jsonld.allowed-context-hosts
Hosts whose JSON-LD @context may be fetched (empty = remote contexts refused; inline contexts always work).
Trust X-Forwarded-* / Forwarded (RFC 7239) from a fronting TLS-terminating proxy.
lws.require-https
false
Refuse to start unless lws.base-uri is https:// (loopback exempt).
lws.hsts.max-age-seconds
31536000
Strict-Transport-Security lifetime, sent only on responses that went out over TLS for an https base URI (0 = off).
lws.tls.enabled
false
Terminate TLS in the server, provisioning a certificate via ACME (bare-Jetty launcher only; ignored, with a warning, by the Spring Boot entry point).
lws.tls.port / lws.tls.http-port
443 / 80
HTTPS port, and the HTTP port serving the ACME challenge + redirect.
lws.tls.acme.directory-url
Let’s Encrypt prod
ACME directory URL (use the staging URL for testing).
lws.tls.acme.domains
base-URI host
Domain(s) to certify (space/comma separated).
lws.tls.acme.email
Contact email for the ACME account.
lws.tls.acme.accept-terms-of-service
false
MUST be true to register (agrees to the CA’s Terms of Service).
lws.tls.acme.renew-before-days / lws.tls.dir
30 / <data>/tls
Renewal lead time; directory for the account key, domain key and certificate.
An empty lws.owners is open mode — every read, write and control decision permitted for every
client, anonymous ones included — and the server only runs that way with lws.dev.open=true. Never
use it on a reachable host.