File Manager

OcaltQL gives full programmatic control over your namespace's filesystem. Every path used with these verbs must start with /root, /mounted, or /external when a server is bound — see Namespace Storage for how those are structured.

Reading and writing

Example
FILE WRITE "Hello World" TO "/root/hello.txt"
(* FILE WRITE "/root/hello.txt" CONTENT  "Hello World". Also works *)
AFTER FILE READ "/root/hello.txt" SET ?content
AFTER EMIT ?content

Binary is bytes, not base64

A binary file read out of the namespace is a bytes value - the actual bytes, the way open(f, "rb").read() gives you bytes in Python or file_get_contents does in PHP. It is not base64, and it is not text that happens to look like data.

Bytes go where bytes belong with no ceremony: written back out with FILE WRITE, sent to the response with EMIT, measured with LENGTH, indexed a byte at a time. Base64 is a transport - what you convert to when a JSON field or a URL cannot carry binary - and it appears only because you asked for it.

Read It, Serve It - No Encoding Anywhere
FILE READ "/root/report.pdf" SET ?bytes
AFTER LENGTH ?bytes SET ?n
AFTER HEADER "Content-Type" AS "application/pdf"
AFTER EMIT ?bytes
(* The response IS the PDF *)

Writing binary - the BASE64 modifier

BASE64 tells FILE WRITE that the content it has been handed is already base64, and that the decoded bytes are what should land on disk. Without it, base64 is written as the literal text it is. This is how a PNG, a PDF, or any other binary arrives in the namespace as a real file, and the encoded form can come from anywhere - a captured screenshot's image_b64, a fetched response, or an ENCODE result.

The modifier goes at the end and works with both spellings: after CONTENT, or after the TO form - FILE WRITE ?png TO "/root/pixel.png" BASE64.

Writing a Real PNG from base64
STRING "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC" SET ?png
AFTER FILE WRITE "/root/pixel.png" CONTENT ?png BASE64
(* AFTER FILE WRITE ?png TO "/root/pixel.png" BASE64. Also works *)
AFTER FILE STAT "/root/pixel.png" SET ?s
AFTER EMIT "wrote " & ?s("size") & " bytes"
(* Emits: wrote 69 bytes — a real 1x1 PNG on disk.
   Without BASE64 the same call writes 92 bytes: the base64 text itself *)
Invalid base64 is refused. Content that is not valid base64 fails with Invalid base64 content. and nothing is written - test !THIS('state') or read !ERROR('message') to catch it. Line breaks inside the base64, and a leading data:image/png;base64, prefix, both count as invalid - strip them before writing.
Binary round-trips. FILE READ returns a string for a text file and a bytes value for a binary one - a PNG read back is the PNG, not null. A bytes value can be written straight out again with FILE WRITE "path" CONTENT ?b (no modifier: it is already binary), measured with LENGTH, indexed a byte at a time with ?b(0), or turned back into text form with ENCODE ?b AS BASE64. The BASE64 modifier is still what you want when the script is holding base64 text - from a screenshot, a fetched response, or an ENCODE result.

Checking and inspecting

Example
FILE EXISTS "/root/hello.txt" SET ?bool
AFTER EMIT ?bool
AFTER FILE STAT "/root/hello.txt" SET ?info
AFTER EMIT ?info

FILE STAT returns an object with exists, is_dir, is_file, size, and modified.

Listing a folder

Example
FILE LIST "/root" SET ?items
AFTER EMIT ?items

FILE LIST returns an array of objects, each with name, is_dir, size, and modified.

Folders

Example
FILE MKDIR "/root/photos"

Moving and copying

Example
FILE MOVE "/root/old.txt" TO "/root/new.txt"
AFTER FILE COPY "/mounted/source.txt" TO "/root/copy.txt"
AFTER FILE COPY "/root/photos" TO "/mounted/photos-backup"

FILE MOVE and FILE COPY both take a file or a folder. A folder is copied with everything inside it, at every depth, and the copy is named by the destination path. A link inside the folder is copied as a link, pointing where the original pointed; it is never followed. A folder cannot be copied into itself or into one of its own subfolders, and FILE COPY refuses that with A folder cannot be copied into itself.

Deleting

Example
FILE DELETE "/root/hello.txt"

Storage usage

Example
STORAGE USAGE SET ?info
AFTER EMIT ?info

Returns an object with a root entry and a mounted entry, measured in bytes. Each carries used, what the location holds now, and limit, its size on your plan. mounted also carries enabled, which is false on Free, where there is no mounted storage and its limit is 0.

Space Left in /root
STORAGE USAGE SET ?info
AFTER CALCULATE ?info("root")("limit") - ?info("root")("used") SET ?free
AFTER EMIT ?free & " bytes free in /root"

Selections

A selection groups multiple paths together so a batch operation can run across all of them at once.

Selecting specific items
FILE SELECT "/root/file1" AND "/root/folder1" AND "/root/file2" SET ?selarray
Selecting everything in a folder
FILE SELECT ALL "/root/folder" SET ?selarray

Acting on a selection

Example
FILE SELECT "/root/a.txt" AND "/root/b.txt" SET ?selarray
AFTER FILE SELECTION ?selarray COPY TO "/root/backup"
AFTER FILE SELECTION ?selarray DELETE

FILE SELECTION ... COPY TO and ... MOVE TO place every selected item into the destination folder, keeping its original name. A selected folder is copied or moved with everything inside it. FILE SELECTION ... DELETE removes every item in the selection.

Compressing and extracting

Example
FILE SELECT "/root/a.txt" AND "/root/b.txt" SET ?selarray
AFTER FILE COMPRESS ZIP ?selarray TO "/root/archive.zip"
AFTER FILE COMPRESS TAR ?selarray TO "/root/archive.tar"

Each selected item is stored in the archive under its own name. A selected file /root/reports/q3.pdf becomes q3.pdf, and a selected folder /root/photos becomes photos/ with its contents beneath it, whichever location it came from, exactly as any archive tool would store them. Extracting the archive anywhere gives back what was selected, with nothing of the namespace layout around it. Links are stored as links. The archive is written fresh: an archive already at the destination is replaced, not added to, and an archive may not be written over one of the files it contains.

Unpacking an archive

FILE EXTRACT is the other half. INTO names the folder the contents should land in, and it is created if it is not there. The format is read from the archive itself, so a .tgz that someone named .zip still opens.

Extract into a folder
FILE EXTRACT "/root/archive.zip" INTO "/root/unpacked" SET ?folder
AFTER EMIT ?folder

It returns the folder it wrote, so the next verb can walk straight into it.

Extract, then read what arrived
FILE EXTRACT "/root/build.tar.gz" INTO "/mounted/builds/latest" SET ?folder
AFTER FILE LIST ?folder SET ?items
AFTER FOREACH ?items SET ?item
OPEN
  EMIT ?item("name") & " - " & ?item("size") & " bytes"
CLOSE

Zip, tar and tar.gz are all read. Nothing has to say which - the archive is inspected, not its name.

Round trip
FILE SELECT "/root/notes.txt" AND "/root/data.csv" SET ?picked
AFTER FILE COMPRESS ZIP ?picked TO "/root/backup.zip"
AFTER FILE EXTRACT "/root/backup.zip" INTO "/root/restored" SET ?where
AFTER EMIT "restored into " & ?where
(* /root/restored now holds notes.txt and data.csv *)

Public sharing

A file can be made publicly accessible at a generated URL under ocalt.com/shared/, without requiring the visitor to have an Ocalt account.

Example
FILE SHARE "/root/file.mp3" SET ?newpublicurl
AFTER EMIT ?newpublicurl
There is no limit on how many files you may share at once. Files inside /root/.ql, the platform's working area, cannot be shared; FILE SHARE refuses them. Copy such a file into your own folders and share the copy.
Listing and removing shares
FILE SHARE LIST SET ?sharedarray
AFTER FILE UNSHARE "/root/file.mp3"
AFTER FILE UNSHARE ALL

QR Code

The QR verb generates a QR code image from a publicly accessible URL. It only accepts shared file URLs (FILE SHARE) or paths inside /root/sites/ - anything else returns an error directing the user to share the file first or move it to a public folder.

QR returns the namespace path of a PNG image inside /root/.ql. The image is named for the URL it encodes, so asking for the QR code of the same URL again rewrites the same file rather than adding another. The image itself is not public. To show it on a page, read it and embed it as a data URL, as in the last example below, or copy it into your own folders and share the copy.

QR from a Shared File
FILE SHARE "/root/report.pdf" SET ?url
AFTER QR ?url SET ?qrpath
AFTER EMIT ?qrpath
QR from a Public Site Path
QR "/root/sites/mysite/index.html" SET ?qrpath
AFTER EMIT ?qrpath
(* The QR encodes https://mysite.ocalt.site/index.html *)
Non-Public URL - Blocked
QR "https://google.com" SET ?qr
OR CATCH ERROR SET ?e
AFTER EMIT ?e("message")
(* "URL not available publicly. Please share the file first
    or place it in a public folder." *)
Showing a QR Code on a Page
FILE SHARE "/root/report.pdf" SET ?url
AFTER QR ?url SET ?qrpath
AFTER FILE READ ?qrpath SET ?png
AFTER ENCODE ?png AS BASE64 SET ?b64
AFTER EMIT "<img alt='QR code' src='data:image/png;base64," & ?b64 & "'>"