sites

The sites service is used to host content for an account and serve it over HTTP to the account's subdomain.

Service docs

SystemService::Sites

struct SystemService::Sites {
    const psibase::AccountNumber service; 

    serveSys(...);    // Serves a request by looking up the content uploaded to the specified subdomain
    storeSys(...);    // Stores content accessible at the caller's subdomain
    remove(...);      // Removes content from the caller's subdomain
    isValidPath(...); // Checks whether a request for content on a site at the given path is valid (such a request will not produce a 404).
    enableSpa(...);   // Enables/disables single-page application mode. When enabled, all content requests return the root document.
    setCsp(...);      // Sets the Content Security Policy for the specified path (or "*" for a global CSP). If a specific CSP is set, it takes precedence over the global CSP. If no specific or global CSP is set, a default CSP is used.
    enableCache(...); // Enables/disables HTTP caching of responses (Enabled by default) Cache strategy: - `If-None-Match` header is checked against the hash of the content - The hash is stored in the `ETag` header - If the hash matches, a 304 Not Modified response is returned - If the hash does not match, the new content is returned with an updated `ETag` header
};

Provide web hosting.

This service provides web hosting to all accounts. It is the default server that handles hosting of the data items specified in psibase packages.

See psibase CLI docs for details on how to upload files and directories.

After files are uploaded, the site is available at http://$ACCOUNT.$DOMAIN

SystemService::Sites::serveSys

std::optional<psibase::HttpReply> SystemService::Sites::serveSys(
    psibase::HttpRequest request
);

Serves a request by looking up the content uploaded to the specified subdomain.

SystemService::Sites::storeSys

void SystemService::Sites::storeSys(
    std::string                path,
    std::string                contentType,
    std::optional<std::string> contentEncoding,
    std::vector<char>          content
);

Stores content accessible at the caller's subdomain.

SystemService::Sites::remove

void SystemService::Sites::remove(
    std::string path
);

Removes content from the caller's subdomain.

SystemService::Sites::isValidPath

bool SystemService::Sites::isValidPath(
    psibase::AccountNumber site,
    std::string            path
);

Checks whether a request for content on a site at the given path is valid (such a request will not produce a 404)..

Note: For single-page applications, static assets (e.g. 'style.css') can be checked normally. However, all other assets are routed client-side, so a route like /page1 is considered a valid route as long as the SPA serves a root document.

SystemService::Sites::enableSpa

void SystemService::Sites::enableSpa(
    bool enable
);

Enables/disables single-page application mode. When enabled, all content requests return the root document..

SystemService::Sites::setCsp

void SystemService::Sites::setCsp(
    std::string path,
    std::string csp
);

Sets the Content Security Policy for the specified path (or "*" for a global CSP). If a specific CSP is set, it takes precedence over the global CSP. If no specific or global CSP is set, a default CSP is used..

SystemService::Sites::enableCache

void SystemService::Sites::enableCache(
    bool enable
);

Enables/disables HTTP caching of responses (Enabled by default) Cache strategy:

  • If-None-Match header is checked against the hash of the content
  • The hash is stored in the ETag header
  • If the hash matches, a 304 Not Modified response is returned
  • If the hash does not match, the new content is returned with an updated ETag header.

Additional design notes

CSP header baseline

The Content-Security-Policy header aids security efforts, as modern browsers restrict the capabilities of the document on the client-side according to the policy expressed in this header. The sites server uses a permissive baseline CSP header that should permit most common requirements.

Default policyValues
default-srcself
font-srcself, https:
script-srcself, unsafe-eval, unsafe-inline, blob: https:
img-src*
style-srcself, unsafe-inline
frame-src*
connect-src*, blob:

Dynamic CSP

If the baseline CSP is insufficient, the sites service allows users to customize the header for their entire site or for a specific path using the setCsp action.

The priority of the CSP header applied to content is given to a header set for a specific path, then the header set for an entire site, and finally will default to the default policy if no custom headers were provided.

HTTP caching

On upload, the sites server calculates and stores a hash of the content at each path. The server now returns the hash in an etag header, as well as setting the cache-control header to no-cache.

This cache policy tells the browser to add, on any subsequent requests for the same content, an If-None-Match header with the previously sent ETag value. The server then has a chance to respond with 304 Unmodified, allowing the browser to use its cached value for the asset, rather than initiating a fresh transfer.

This helps reduce the load on the server and speeds up subsequent page loads.

Compression

Uploading assets to a psibase network using the the psibase CLI will automatically apply Brotli compression to content of a supported mime type.

Automatically compressed types:

  • text/plain
  • text/html
  • text/css
  • application/javascript
  • application/json
  • application/xml
  • application/rss+xml
  • application/atom+xml
  • image/svg+xml
  • font/ttf
  • font/otf
  • application/wasm

Compression level can be specified in every CLI where it makes sense:

  • psibase boot -z <LEVEL>
  • psibase upload -z <LEVEL>
  • psibase install -z <LEVEL>
  • cargo psibase install -z <LEVEL>

The compression level ranges from 1-11, where 1 is the fastest and 11 is the best compression. The default value is 4.

Fallback decompression

If the client uses the Accept-Encoding HTTP request header to indicate that it does not support the encoding type of the requested content, and the server knows how to decompress the content, then the server will automatically decompress the content before serving it to the client.

The only encoding type that currently supports fallback decompression is Brotli.