| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [View Raw Code] [Original HTTPS Page] |
JSS uses restrictive mode by default: if no ACL file exists for a resource, access is denied. This prevents unauthorized writes to unprotected containers.
You must create a root .acl file in your data directory. Example (JSON-LD format):
{
"@context": {
"acl": "http://www.w3.org/ns/auth/acl#",
"foaf": "http://xmlns.com/foaf/0.1/"
},
"@graph": [
{
"@id": "#owner",
"@type": "acl:Authorization",
"acl:agent": { "@id": "https://your-domain.com/profile/card#me" },
"acl:accessTo": { "@id": "https://your-domain.com/" },
"acl:default": { "@id": "https://your-domain.com/" },
"acl:mode": [
{ "@id": "acl:Read" },
{ "@id": "acl:Write" },
{ "@id": "acl:Control" }
]
},
{
"@id": "#public",
"@type": "acl:Authorization",
"acl:agentClass": { "@id": "foaf:Agent" },
"acl:accessTo": { "@id": "https://your-domain.com/" },
"acl:default": { "@id": "https://your-domain.com/" },
"acl:mode": [
{ "@id": "acl:Read" }
]
}
]
}Save this as data/.acl (replacing your-domain.com with your actual domain).
See Issue #32 for background.
Paths whose segments start with a dot are refused with 403 unless the segment is one of the Solid-specific names (.well-known, .acl, .meta, .pods, .notifications, .account). This keeps .git/, .env, .htpasswd and similar files out of reach over HTTP even when they sit inside a pod. Git smart-HTTP, the pay routes and app plugins mounted at a dot prefix are allowed through on their own terms.
The check runs on the percent-decoded path, segment by segment, and the storage lookup applies the same rule, so %2Egit is .git on both sides and neither can be talked past the other (#639). A dot has to be literal in the request: a segment that only starts with a dot after decoding is refused even for the allowed names. A path that cannot be decoded, or that hides a path separator behind %2F, is refused with 400 instead of being passed on.
If-Match and If-None-Match on PUT, DELETE and PATCH are checked and acted on as one step. Writes to a resource are queued within the process and held under a lock file next to the resource (.<name>.lock, never listed or served) across processes that share the data directory. Two clients racing with the same, still valid, ETag get one 2xx and one 412; the loser's write is never silently overwritten. A request that cannot take the lock within 30 seconds gets 503 with Retry-After: 1.
Authentication can make the server fetch a document it does not control: a CID document for LWS-CID tokens, a DID document and WebID back-link for did:nostr, or a WebID profile for WebID-TLS. Every such fetch runs under one deadline that covers the connection, the response headers and the body read, plus a byte cap. The CID and did:nostr fetchers follow redirects themselves and apply the 5-second deadline per hop; the WebID-TLS profile fetch lets fetch follow redirects and applies one 5-second deadline to the whole chain. In addition, the CID and did:nostr fetches also go through the SSRF guard (validateExternalUrl) on the original URL and on every redirect hop, with cross-origin redirects refused. A host that answers promptly and then trickles the body cannot hold an authentication attempt, or the outbound socket, open beyond the deadline: the fetch fails, the failure is cached (1 minute for CID and did:nostr lookups, 5 minutes for a WebID-TLS verification), and the request is answered as unauthenticated. Responses that are not read (redirect hops, error statuses, oversized bodies) are cancelled so the socket is released at once.
By default, JSS uses path-based pods (/alice/, /bob/). This is simple but has a security limitation: all pods share the same origin, making cross-site scripting (XSS) attacks possible between pods.
Subdomain mode provides origin isolation - each pod gets its own subdomain (alice.example.com, bob.example.com), preventing XSS attacks between pods.
| Mode | URL | Origin | XSS Risk |
|---|---|---|---|
| Path-based | example.com/alice/ | example.com | Shared origin - pods can XSS each other |
| Subdomain | alice.example.com/ | alice.example.com | Isolated - browser's Same-Origin Policy protects |
jss start --subdomains --base-domain example.comOr via environment variables:
export JSS_SUBDOMAINS=true
export JSS_BASE_DOMAIN=example.com
jss startYou need a wildcard DNS record pointing to your server:
*.example.com A <your-server-ip>
| Path Mode | Subdomain Mode |
|---|---|
| example.com/alice/ | alice.example.com/ |
| example.com/alice/public/file.txt | alice.example.com/public/file.txt |
| example.com/alice/#me | alice.example.com/#me |
Pod creation still uses the main domain:
curl -X POST https://example.com/.pods \
-H "Content-Type: application/json" \
-d '{"name": "alice"}'| Back | FazBrowse Home | New Git URL |