KB

Common pitfalls, recurring AI-agent misconceptions, API misuse and their resolutions. Each entry is an authoritative reference you can link to when the same issue comes up again.

Path tra­ver­sal

Docly's filesystem API accepts only absolute paths from site root — relative path components are not supported, so the path-traversal vulnerability class cannot be expressed as input.

Crea­ting new doc­ly fi­les

New .docly and .json files must be written atomically — build the complete content in a temp folder on a separate drive and copy it into the Docly drive. Direct writes from external tools produce unregistered file stubs and burn the filename.

Hash fi­les are cac­hed

Hash files render once and are cached, so per-request expressions like #docly.foo(request.Jwt.x)# freeze the first visitor's data and serve it to everyone. Dynamic per-user data must come from #/API/-endpoints fetched by browser JS at runtime.

Use til­de paths in Ja­vaSc­rip­t

Hardcoded leading-slash paths in JavaScript break the moment the app is published under a subfolder. On publish, Docly rewrites href and src attributes in static HTML — but not form action, and nothing JS emits at runtime. Use ~/ in all JS path strings and in any non-href/non-src HTML attribute that holds a path; it resolves to the current webapp's base path at runtime and works equally at the root.

Omit fi­le ex­ten­sion­s in link­s

Docly serves every page at a clean, extensionless URL and resolves the underlying file by name, so internal links must omit the .html and .hash suffix. A link that includes the extension still resolves — it is not broken — but it produces a non-canonical, noisier URL and splits SEO signals between two addresses for the same page.

Sort fol­der con­tents by a cus­tom fiel­d

Make a folder's contents sortable by adding a CustomColumns entry to the folder's .docly properties: a Formula that reads a field from the contained documents, with Sorting set to "Sortable". Wrap numeric sort fields in parseInt() so they sort by value, not as text.

Gi­ve lin­ked ima­ges SEO-friend­ly fi­lena­mes

When you link an embedded image through Docly's scaling URL, the last path segment — the "virtual name" — becomes the image's public filename but is ignored when Docly resolves the file. Set it to a descriptive, keyword-bearing slug (commonly the page's own title via encodeURIComponent(Title)) so every generated image is served under an SEO-friendly name instead of a throwaway like image.jpg or photo-10.jpg.

Ship se­cure user ac­cess fast: let Doc­ly hand­le lo­gin, you hand­le the per­mis­sio­ns

Skip the authentication tax — let Docly's built-in login handle passwords, resets, sessions, 2FA and threat protection for you (enterprise-grade security in minutes, no auth code to write or maintain): invite users with docly.addFolderShare("/", email, "V", inviteMessage) and let Docly's login portal handle authentication, passwords, resets and sessions, while application rights (roles, per-customer access) live in a local user-registry folder with one document per email address, looked up via request.user in every API script.

Rea­ding que­ry and form fields in API call­s

Docly API endpoints (#/API/*.js) do not receive an Express-style request object — input arrives via auto-bound function parameters and the globals query (URL query string) and form (request body, JSON parsed for you).

Build client UI as st­ruc­ture­d web com­ponen­ts

When a client-side Docly solution grows beyond a single page, structure the UI as native Web Components (custom elements) instead of one large script that manipulates the DOM by hand. Encapsulating markup, behavior and state inside reusable elements keeps the frontend maintainable, testable and portable across pages — and works without any framework or build step.

Keep pri­vate da­ta un­der the # fol­der

Anything in a published folder is served directly over HTTP — including raw JSON documents and data files. To keep data out of public reach, place it under the # folder, whose contents are never served as static files even when a parent folder is published.

Group API end­point­s in­to to­pic fol­der­s

Dumping every endpoint as a flat list of .js files directly in #/API/ becomes unreadable and collision-prone as an app grows. Docly maps any subfolder under #/API/ straight to the URL, so #/API/orders/create.js is reachable at /API/orders/create. Group related endpoints into topic folders (orders, users, billing) to get readable URLs, fewer naming clashes, and a structure that scales with the application.

Use mas­ter pa­ges for sha­red layou­t

When several pages or display templates share the same layout — header, footer, navigation, common CSS/JS — define a single master page (e.g. #/master.hash) and have the other pages inherit from it with the MASTER directive. Repeating the full HTML chrome in every page leads to drift, inconsistent styling, and tedious multi-file edits.

Or­gani­ze pa­ges in­to to­pic fol­der­s

Dropping every .html and .hash file into a single folder produces a long, flat, unbrowsable list and flat, meaningless URLs as a site grows. Because Docly maps a file's path under the site straight to its URL, organising pages into topic folders (products, support, about) gives readable, RESTful URLs, clean section landing pages via Index files, and a structure that scales with the site instead of fighting it.

Keep in­dex.­has­h out of con­tent fol­der­s

index.hash is a fallback-resolved file: a single #/Folder/index.hash renders the listing for every folder, and #/Root/index.hash is served at the site root as the home page. Place index.hash files in the developer-only #/ tree instead of inside content folders — a copy next to the documents it renders means one template per folder, visible and editable to non-developer users, drifting out of sync with every layout change.

Se­para­te co­de from con­tent for non-de­velo­per user­s

When non-developer users will administer data in a Docly solution, move display assets (index.hash, shared CSS/JS, masters, partials) into #/Root and #/Folder. The #/ tree is invisible to anyone without developer access, so this keeps the user's workspace free of code files they shouldn't touch.

Use AVIF and WebP ima­ge for­mat­s

You keep uploading JPEG/PNG as before; Docly converts on serving, and the file extension on the display name in the image URL decides the output format (…/1200x800x0/produkt.webp ⇒ WebP). WebP has universal browser support, so <img src="….webp"> is enough on its own. AVIF must sit inside a <picture> with WebP/JPEG fallback, because Docly does not negotiate format via the Accept header — without a fallback, visitors without AVIF support see nothing. A safe site-wide default is images.defaultFormat: webp in #/site.json; avoid avif as a global default.

Rest­rict and re­gis­ter ima­ge si­zes

The scaled image URL (/foto.jpg/1200x800x0/navn.jpg) is open by default: every unique size is a cache miss with heavy scaling and a new cache file on disk. A size policy locks scaling to a known set of sizes — and it is also what unlocks WebP/AVIF, which are refused on the URL path until the policy is set to auto or strict.

Cac­he-bus­ting bund­les with as­setUr­l

Browsers cache static files, so an unchanged bundle URL can serve stale JS/CSS for up to a day after a publish. Wrap your own <script src> and <link href> in assetUrl() to append a per-publish ?v= stamp that busts the cache. Never use assetUrl on dynamic or per-user output — ?v= URLs get max-age=1 year immutable, which would freeze the first dynamic response for everyone.

Op­timi­ze pa­ge per­for­man­c­e

When a Docly page scores poorly on PageSpeed Insights (PSI) mobile, use Claude Code and the Claude Chrome plugin to diagnose which bottleneck actually applies before fixing anything. The single most important thing is measurement: local Lighthouse reported 47-98 on identical code on dops.no, and PSI scores are bimodal, so use the PSI API, run 12+ measurements per variant and compare fast-run fractions against a control - never a median of five. This article describes that workflow, the candidate fixes (defer heavy end-of-body JS to after load + idle, self-host the font, shrink/preload the LCP image, defer analytics and canvas effects, pin effect elements to avoid CLS), how to work safely on a live site, and four Docly-specific traps that generic web-perf guides never mention. Measured on dops.no: deferring end-of-body JS is the useful lever, decorative effects deferred to idle are effectively free, and removing 245 KB of third-party JavaScript is worth about one point - so decide those on UX grounds, not score grounds.

Work sa­fely on a li­ve si­te

On Docly the folder on disk is production - no git, no staging, no build step - so pointing an AI agent straight at a live site is extremely productive and instantly public. That trade is sensible on a low-traffic marketing site and reckless on a high-traffic, transactional or login-protected one. This article covers the practices that make it sustainable: back up before editing anything you did not create, know the blast radius (a new page is safe, a shared static asset reaches every visitor and outlives the fix via cache), never write JavaScript into production files via shell escaping, experiment on noindex throwaway pages rather than gating shared files, and verify with a cold cache - because your own browser serves the cached previous version and will show you a broken site working perfectly.