Sign-in and access Last updated: 23. Oct 2026
An overview of everything that decides who can see what on a published site — what is public by default, Docly sign-in, shares, the site router with your own sign-in, access checks in API scripts and the security level. Each part links to its detailed page.
At a glance
Mechanism | Decides | Set in |
|---|---|---|
What is public by default | What a publication serves at all | Where you put the files — |
Security level | Whether a visitor must pass a human check first |
|
Docly sign-in | Whether a Docly account is required | Require login on the publication ( |
Docly shares | Which signed-in users get in, and to which folders | The share dialog, or |
The site router | Every request, in your own code — your own sign-in, API tokens |
|
API scripts | What data each caller gets, and what they may change | Your code: |
The order a request meets them: the firewall and the security level come first. On a publication without sign-in the router runs next, before the page cache. On a publication with sign-in, Docly's sign-in and access check come first, and the router runs after them, for signed-in users only — it can narrow access, never widen it.
What is public by default
On a publication without sign-in, everything the folder serves is public: pages, uploaded files by their path, scaled images, and .docly documents rendered through a display template for their schema (#/<Schema>.hash or .html, or a schema template that allows publishing). A document whose schema has no display template answers 404.
#/answers 404. A request for any path under#gets the 404 page, with two kinds of exceptions: files in#/Root/and#/Folder/are served at the site's own addresses, and scripts in#/API/answer at/API/…. Everything else under#— data, configuration, schemas,#/Services/with the router — is only for server-side code.Tool folders answer 404 at any depth:
.claude,.git,.svn,.hg,.vscode,.vs,.ideaand.cursorby default. A request for one counts against the visitor's IP as a probe.An app is served the same way. Its root is the site's
#, so itsRoot/,Folder/andAPI/folders are public in the same way, and the rest of it is not.
Docly sign-in on a publication
Turn on Require login in the publish dialog (authMode 2; 3 is accepted and works the same). Every request to the publication then needs a Docly session. A page view is sent to the Docly sign-in page and back; a background call that does not accept text/html, such as fetch, gets 401 with {"ResultCode":401,"Message":"Login required.","LoginRequired":true}.
After sign-in, the visitor needs Visit access — through a share — to the file or folder they open. A user without access is sent back to the sign-in page with a message that their user has no access to the page. In API scripts, request.user holds the signed-in user's e-mail address. Pages are cached and shared by all signed-in users, so never render per-user data in a .hash page — see Hash files are cached.
Details on publishing with sign-in: Publishing a site.
The sign-in page and Customizable sign-in
The sign-in page is Docly's: the visitor signs in with their Docly account, and the page shows the publication's verified address under to continue to. If the workspace's subscription includes Customizable sign-in, a published folder can add its name and logo in a small card there — never colours, backgrounds or free text.
Set it with Customizable sign-in page under Require login in the publish dialog. Docly stores it in the site's own #/login.json; turning the switch off writes "enabled": false and keeps the name and logo. The name is cut at 60 characters, and the logo is a path in the site to a PNG, JPG or WebP image of at most 256 KB, looked up in the site and then in the app. Without the feature on the subscription, the file has no effect.
{ "name": "Customer portal", "logo": "/assets/logo.png" } See Site.json config file for the login section and how it is inherited from the app.
Docly shares
With sign-in on, the shares decide who gets in. In the share dialog, Web access gives Visit only: the user sees the published pages, but not the content in Docly. Read only, Read and write and Admin include Visit, and Block shuts a user out even when access is given in a folder above or to everyone. A share applies to the folder and everything below it, so a share on a subfolder opens that part of the site. Someone without a Docly account gets an invitation to create one.
From an API script, addFolderShare creates a share and sends the invitation — docly.addFolderShare("/", email, "V", message) gives web access to the whole site. removeFolderShare, getFolderShares and addFileShare do the rest. They run as the site's publisher, so the endpoint that calls them must check who is asking.
Your own sign-in: the site router and JWT
For your own identity provider, or for machine calls with a token, publish without sign-in and let the site router protect the site: one script in #/Services/ that runs before every request — pages, files, images and API calls — and lets it through, sends it to sign-in or refuses it. Start from deny: name what is public, and require the session for the rest.
When the sign-in completes, the router writes the session with writeJwt; later requests read it as request.Jwt, in the router and in API scripts. deleteJwt ends it, and return docly.denyAccess() in an API script answers 401 (denyAccess). The token is signed with a secret per site, not encrypted, and only valid on the day it was issued. Pages never see request.Jwt. A router that fails answers 500, never the page.
// #/Services/Router.js — the publication has NO sign-in
const PUBLIC = ['/assets/', '/auth/']; // /auth/login and /auth/callback: your sign-in, see The site router
export default (route) => {
if (PUBLIC.some(p => request.url.startsWith(p))) return route.next();
if (request.Jwt) return route.next();
return route.redirect('/auth/login?returnUrl=' + encodeURIComponent(request.url));
}; The full sign-in flow and token checks: The site router and Use the site router for your own sign-in and API tokens. On a publication with Docly sign-in, docly.logOut() ends the Docly session instead (logOut).
Checking access in API scripts
API scripts read and write with the site's rights, not the visitor's: docly.saveFile, patchFile and deleteFile write wherever they are told. The check is your code. Decide who is calling from request.user (Docly sign-in) or request.Jwt (your own sign-in), ask what the visitor's shares allow with allowVisit, allowRead and allowModify, and check the document you actually write.
// #/API/save-note.js — on a publication with Docly sign-in
export default (path, text) => {
if (request.method != "POST") throw new Error("Verb not supported");
if (!docly.allowModify(path)) return docly.denyAccess(); // the same path as the write
return docly.patchFile(path, { Text: text });
}; Security level and Under attack
The security section of #/site.json (or #/security.json, inherited from the app) decides which visitors must pass a Cloudflare Turnstile check before the site answers anything: normal (nobody), elevated (probes, flagged addresses and low-trust countries), high (everyone except high-trust countries) and underAttack (everyone). The check runs before the page cache and before the router, so cached pages are covered too.
{ "security": { "level": "underAttack", "until": "2026-11-01T12:00", "exclude": ["/api/webhooks/*"] } } From high up, search engines and machine clients are stopped as well: set until so the level ends by itself, and put webhooks and other machine calls in exclude. Single published documents are not covered. See Site.json config file and Set a site's security level in site.json.
Which to use
You want | Use |
|---|---|
Public pages, private data | A publication without sign-in, and the data under |
Customers or partners who sign in with a Docly account | Require login and shares with web access; check rights in API scripts with |
Your own users, your own identity provider, or API tokens | A publication without sign-in, the site router and |
A site that is being flooded by bots | A higher security level, with |