Skip to content

In subdomain mode, files at the base-domain root are unreachable (401) #307

Description

@melvincarvalho

Problem

With subdomains: true + baseDomain: solid.social, any file at the base domain's root returns 401 even with a public-read ACL. The root container itself (GET /) works; direct file access (GET /mashlib.js, /index.html, /terms.html) does not.

Root cause

buildResourceUrl in src/auth/middleware.js rewrites the resource URL when the request hits the base domain:

if (subdomainsEnabled && hostname === baseDomain && !podName) {
  const pathMatch = urlPath.match(/^\/([^/]+)(\/.*)?$/);
  if (pathMatch && !pathMatch[1].startsWith('.')) {
    const podName = pathMatch[1];
    const remainder = pathMatch[2] || '/';
    return `${protocol}://${podName}.${baseDomain}${remainder}`;
  }
}

For GET solid.social/mashlib.js:

  • Path matches → podName = mashlib.js
  • Returns https://mashlib.js.solid.social/
  • ACL check is now against a domain that doesn't exist
  • Root ACL (accessTo: https://solid.social/) doesn't match → 401

Reproduction

On a JSS with subdomains: true + baseDomain set:

# Works — base domain container
curl -I https://DOMAIN/       # 200

# All fail — base domain files
curl -I https://DOMAIN/mashlib.js    # 401
curl -I https://DOMAIN/index.html    # 401
curl -I https://DOMAIN/any-file.txt  # 401

Impact

Core assets (mashlib.js, mashlib.css) hosted at the base domain become unreachable, breaking the data browser for pods on that server. Operators can work around by moving assets to a subdomain or CDN, but the default setup is broken.

Proposed fix

Only treat a path segment as a pod name if it can't be served directly. Options:

  1. Check file existence first — if data/${segment} exists as a file, serve it directly
  2. Pattern exemption — treat anything with a file extension (.js, .css, .html, etc.) as a file, not a pod
  3. Explicit allow-list — operators can list reserved root paths

(1) is the most correct but requires storage lookup in the auth middleware. (2) is a fast pragmatic fix: if (pathMatch[1].includes('.')) → don't rewrite.

Context

Found while debugging why mashlib wasn't loading on solid.social. mashlib.js.acl and mashlib.css.acl were both present with public-read rules but still returning 401 — because the rules apply to https://solid.social/mashlib.js but the ACL checker was comparing against https://mashlib.js.solid.social/.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions