Docs
Install Quay, publish a folder, and point production at a release. Data, sign-in, functions and the rest each have a page under Layers.
Quickstart
You need Node 20 or newer and a checkout of this repository. The package is not on the npm registry. The account is on testnet, and the faucet creates it. Keys stay in ~/.near-credentials/testnet/ and are not written into the project.
npm install -g ./cli quay init quay deploy ./dist -m "harbour office" quay promote 1 quay open
quay init writes quay.json with the account, the testnet RPC, and the gateway. Later commands read that file from the current directory or a parent.
The preview for release 1 is https://<account>.testnet.page/r/1/. After promote, the same files are also at the host root.
Use the account host only. An extra host label, such as www.<account>.testnet.page, fails TLS.
Commands
This reference is generated from the command help in the quay-near package.
quay — publish a site to NEAR testnet
Usage
quay <command> [options]
Commands
init Create or adopt a testnet account, deploy the contract, and write quay.json.
deploy Upload a folder as an immutable release and print its preview URL.
promote Make a release the production site.
rollback Swap production with the previous release.
releases List releases: id, date, files, size, message, and which is production.
status Show the account, production pointer, storage, and gateway.
gateway Serve the account's site on this machine over RPC, without testnet.page.
open Print a preview URL. On macOS, also open it.
delete Delete a release that is not production and unlock its storage.
keys Create, list, or revoke a publish-only key.
db Read and write the site's document database: collections, docs, and the storage pool.
members List members or open or close joining.
products Define what visitors can pay for, in NEAR or one token.
receipts List payment receipts, newest first.
functions Build, deploy and call server functions on OutLayer (Rust, wasm32-wasip2).
secrets Set function secrets. Values are read from stdin or a hidden prompt and encrypted here.
relay Run or fund the relayer that pays gas for member writes (serve, status, create).
up Apply quay.json: collections, products, members, then deploy site.dir and promote.
cron Scheduled functions, kept alive by the contract itself.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Storage is about 0.01 NEAR per 1000 bytes on the contract account.
Run `quay <command> --help` for one command.
Create or adopt a testnet account, deploy the contract, and write quay.json.
Usage
quay init [account]
Options
--replace Deploy the bundled wasm over an existing contract. Asks first.
--yes Skip the confirmation.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Upload a folder as an immutable release and print its preview URL.
Usage
quay deploy [dir] [-m message]
Options
-m, --message <text> Release note, at most 200 characters.
--id <id> Release id. The default is the next number.
--spa Serve index.html for unknown paths inside this release.
--dry-run Print the storage estimate and do not send anything.
--yes Skip the confirmation. A non-interactive shell does not ask.
--ci Sign with the publish-only key.
--store <name> Where files over files.external_over go. fastfs (default): FastNEAR FastFS, no signup, gas only, files up to 1,400,000 bytes, exercised live for a 406 KB wasm. nearfs: no signup, gas only, but the testnet gateway has not served new uploads, so deploys fail the read-back. pinata (needs PINATA_JWT) and filebase (needs FILEBASE_KEY, FILEBASE_SECRET, FILEBASE_BUCKET) need an account and were not exercised live.
--external-over <bytes> Store files larger than this off chain. Overrides files.external_over in quay.json. index.html always stays on chain.
--no-verify Do not read large files back from the store before sealing. Prints a warning.
--verify-wait <sec> How long to wait for the store to serve a large file. The default is 90.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
routes in quay.json are sealed with the release. Large files are checked by sha256 before sealing.
Make a release the production site.
Usage
quay promote <id>
Options
--yes Skip the confirmation.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Swap production with the previous release.
Usage
quay rollback
Options
--yes Skip the confirmation.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
List releases: id, date, files, size, message, and which is production.
Usage
quay releases
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Show the account, production pointer, storage, and gateway.
Usage
quay status
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Serve the account's site on this machine over RPC, without testnet.page.
Usage
quay gateway --account <id> [--port 8787] [--host 127.0.0.1] [--rpc <url>...]
Options
--port <n> Port to listen on. The default is 8787.
--host <addr> Interface to listen on. The default is 127.0.0.1.
--rpc <url> RPC endpoint. Repeat for a fallback list. The default list is fastnear, intea, drpc.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Print a preview URL. On macOS, also open it.
Usage
quay open [id]
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Delete a release that is not production and unlock its storage.
Usage
quay delete <id>
Options
--yes Skip the confirmation.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Create, list, or revoke a publish-only key.
Usage
quay keys <create|list|revoke|prove>
Options
create --ci Add a key that can only call put_chunk and seal. The secret is printed once.
list Show access keys. Secrets stay in the credentials folder.
revoke <public-key> Remove an access key. Refuses the key that is signing.
prove Relay a promote signed by the publish-only key and expect the refusal.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Read and write the site's document database: collections, docs, and the storage pool.
Usage
quay db <subcommand>
Options
collections List collections with their rules and counts.
collection set <name> Create or update a collection. Owner only, 1 yoctoNEAR. Rules not passed keep their value, or default to write owner, payer writer, 4096 bytes.
--write <who> owner, anyone, or members: who may add docs.
--payer <who> writer (the writer attaches a storage deposit) or site (the pool pays, a visitor needs no NEAR).
--max-bytes <n> Largest doc, 1 to 16384 bytes of JSON text.
--moderated The site owner may hide or remove any doc. Pass --moderated=false to turn it off.
--max-per-account <n> Docs one account may hold. none removes the cap.
--on-add <fn> Function run on every add. none removes it. Functions are not built yet.
collection drop <name> Drop a collection that has no docs. Owner only, asks first.
list <collection> List docs. Options: --owner <account>, --prefix <text>, --from <seq>, --limit <1-50>, --order asc|desc, --hidden to include hidden docs.
get <collection> <id> Print one doc.
add <collection> <json|@file.json> [--id <id>] Add a doc. A writer-paid collection gets a storage deposit estimated from the data length at 0.01 NEAR per 1000 bytes; the contract refunds the excess. --deposit <NEAR> sets it. All-digit ids are reserved for automatic ids.
update <collection> <id> <json|@file> Replace a doc you own. Attaches only the storage growth.
rm <collection> <id> Remove a doc you own, or any doc as the site owner of a moderated collection. Asks first.
hide|unhide <collection> <id> Hide or show a doc in a moderated collection. Owner only, 1 yoctoNEAR.
fund <NEAR> Add at least 0.01 NEAR to the storage pool that pays for site-paid docs. Anyone can.
withdraw <NEAR> Release unused pool NEAR. Owner only.
pool Show the pool balance and what site-paid docs hold.
--dry-run Print the call and deposit and send nothing.
--yes Skip a confirmation.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
List members or open or close joining.
Usage
quay members <list|open|close>
Options
list Show whether joining is open and every member account.
open Let anyone join as <name>.<site>. Owner only, 1 yoctoNEAR.
close Only the site owner can add members. Owner only.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Define what visitors can pay for, in NEAR or one token.
Usage
quay products <set|list|rm|token>
Options
set <id> --name <text> --price <NEAR> --grant <pin|receipt> Create or replace a product. Owner only, 1 yoctoNEAR.
--price <NEAR> Price in NEAR, such as 0.1.
--price-token <amount> Price in the smallest unit of the accepted token. Pass either price or both.
--grant <kind> pin pins a doc, receipt only records the payment.
--pin-seconds <n> How long a pin lasts, 1 to 31536000. Required for pin.
list Show products and the accepted token.
rm <id> Remove a product. Owner only, asks first.
token <account|none> Set the NEP-141 token accepted for token prices. Owner only.
--dry-run Print the call and send nothing.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
List payment receipts, newest first.
Usage
quay receipts [--owner [account]]
Options
--owner [account] Only this buyer. With no account, the signing account.
--from <seq> Continue from this cursor.
--limit <1-50> Page size.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Build, deploy and call server functions on OutLayer (Rust, wasm32-wasip2).
Usage
quay functions <deploy|list|invoke|logs|rm|key>
Options
deploy <dir|name> cargo build --release --target wasm32-wasip2, upload, register the OutLayer project <site>/<name>, fn_set when the contract has it.
--store <fastfs|release> fastfs (default, gas only) or release (writes <site.dir>/fn/<name>.wasm, served after the next deploy, 0.01 NEAR per KB).
--name <name> Function name for a folder. Default: the folder name.
list Functions on OutLayer, in quay.json, and on the contract.
invoke <fn> [json] Run it: fn_call on the contract when it has functions, else HTTPS with a saved payment key.
--https Always call over HTTPS with a payment key.
logs <fn> Contract results (_fn) and payment-key usage.
rm <fn> Remove the contract entry, OutLayer project and secrets. Lists what it deletes and asks first.
--contract-only rm: remove only the contract entry (fn_remove).
--include-relay rm: allow removing the relay deployed by quay relay deploy. Without it rm refuses: members could no longer write.
key [--projects a,b] [--usd 0.05] Make a payment key scoped to these functions, fund it with test USDC, save it owner-only. The key is not printed.
--max-per-call <usd> Spend cap per call. Default 0.01.
--dry-run Print the plan and send nothing.
--yes Skip a confirmation.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Functions run in OutLayer TEE workers: about 0.001 NEAR per contract run, 4 to 8 s over HTTPS.
Build with QUAY_CARGO to wrap cargo (for example a build lock).
Anyone who reads a payment key can spend it: scope it to named functions and keep the balance small.
Set function secrets. Values are read from stdin or a hidden prompt and encrypted here.
Usage
quay secrets <set|list|rm>
Options
set <NAME> [NAME...] [--fn <fn>] Store values for a function. One value per name from stdin (one per line), or a hidden prompt. Never on the command line.
--replace Drop names the function held before. OutLayer keeps one encrypted profile per function.
list Names only. Values cannot be read back.
rm --fn <fn> Remove every secret of a function. Asks first.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
Only ciphertext leaves this machine. The keystore key request carries placeholder values with the real names.
Secrets open only for the site contract (contract calls) or the site's payment key (HTTPS calls).
Run or fund the relayer that pays gas for member writes (serve, status, create).
Usage
quay relay serve [--port 8788] [--relayer <account>] | quay relay status | quay relay create [--near 0.5] | quay relay deploy [--key-file <path>]
Options
--port <n> serve: port to listen on. The default is 8788.
--host <addr> serve: interface. The default is 127.0.0.1.
--relayer <account> The relayer account. The default is relay.<site>.
--origin <urls> serve: extra allowed browser origins, comma separated. The site's own origin and http://127.0.0.1:* are always allowed.
--per-account <n> serve: relays per member per hour. Default 30.
--daily-tgas <n> serve: gas budget per UTC day. Default 30000.
--trust-proxy serve: read the client address from X-Forwarded-For.
--fastdata serve: also relay __fastdata_kv, gas-only public data (quay/ keys only). Opt in; off by default.
--near <NEAR> create: NEAR moved from the site account to the relayer. Default 0.5.
deploy Run the relayer as an OutLayer function instead: RELAY_KEY becomes an encrypted secret, no server.
--key-file <path> deploy: the relayer key file. Default ~/.near-credentials/testnet/<relayer>.json. Never printed.
--policy <json> deploy: policy overrides, such as {"methods":["db_add"],"per_account_per_hour":10}.
--dry-run create: print the plan, create nothing.
--yes create: skip the confirmation.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
quay relay serve is the one off-chain piece of a Quay site. Members write through it with zero NEAR; the relayer balance is the hard spend limit.
Relayed writes attach no deposit, so they work for site-paid collections. The key lives in ~/.near-credentials/testnet/<relayer>.json.
Apply quay.json: collections, products, members, then deploy site.dir and promote.
Usage
quay up [-m message]
Options
-m, --message <text> Release note, at most 200 characters.
--dry-run Print the plan, create nothing. Validates the folder, routes and templates.
--yes Skip the confirmation.
--store <name> Override files.store: fastfs, nearfs, pinata, or filebase.
--external-over <bytes> Override files.external_over.
--no-verify Do not read large files back before sealing.
--id <id> Release id. The default is the next number.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
quay.json keys are strict: an unknown key is an error.
Versioned per release: files, routes and templates. Rolling back restores all three.
Not versioned (live state): docs, collection rules, products, members, function code and secrets, relay and cron settings. quay up and quay rollback print this.
payments.token and payments.products set the accepted token and products (a top-level products section still works).
relay host node prints the quay relay serve command. functions, relay host outlayer and cron are applied when this build and the contract support them, else quay up prints a not applied line. cron_arm is never run by quay up: it costs about 1 NEAR liquid.
Scheduled functions, kept alive by the contract itself.
Usage
quay cron <add|list|rm|status|arm|kick>
Options
add "<every 30m>" <fn> Add a job. "every 30m" or "every 2h", at least 5 minutes, at most 8 jobs. --input <json> is passed to the function (at most 4096 bytes), --name <job> names it. Owner only, 1 yoctoNEAR.
list Show jobs with last run and next due.
rm <name> Remove a job. Owner only.
status Show whether the schedule is armed, hops, and last tick.
arm Start the schedule: sends 1 PGas, about 1 NEAR liquid while it runs (unused gas is refunded). That lasts about 15 hours with no jobs and about 6 with one hourly job, since each run takes 95 Tgas; then arm again. Asks first, refuses under 1.1 NEAR liquid.
kick Run due jobs now by resuming the pending step. Anyone can.
--dry-run Print the call and send nothing.
Global options
--json Print one JSON object on stdout.
--account <id> Contract account. Overrides quay.json and QUAY_ACCOUNT.
--rpc <url> JSON-RPC URL. The default is https://test.rpc.fastnear.com.
--help Show this help.
CI
Create a publish-only key on your machine:
quay keys create --ci
The secret is printed once. Store it as the GitHub Actions secret QUAY_CI_KEY, and the account id as QUAY_ACCOUNT. Do not commit the key.
The composite action in action/ runs quay deploy --ci --yes. The example workflow is examples/harbour/.github/workflows/preview.yml. A job that uses that key can upload a preview. quay promote exits 3 and does not send the call.
How it works
The contract account is the only account that can write. put_chunk stores up to 200,000 bytes under their sha256. seal records a release: the file list, the message, the byte count, and whether unknown paths should serve index.html. A chunk used by two releases is stored once. If sealing fails, an owner key drops the chunks that deploy just uploaded. A publish-only key cannot drop them, because that call needs 1 yoctoNEAR.
promote sets the production id and keeps the previous id. rollback swaps them. delete removes a release that is not production. When the last reference to a chunk is gone, the bytes are removed and the locked NEAR unlocks on the contract account. It is not transferred to you.
The public gateway calls web4_get. Production responses use public, max-age=0, must-revalidate. A path under /r/<id>/ uses a year-long immutable cache. A folder path serves the index.html inside it. If the release was sealed with --spa, an unknown path serves the root index.html. Otherwise 404.html is served with status 404 when that file exists.
NEAR locks 0.01 NEAR per 1,000 bytes of storage, about 1 NEAR per 100 KB, plus a small amount for each trie record. The CLI adds 80 bytes per new chunk and 200 bytes for the release when it prints the estimate. Gas is small next to that lock. The contract that quay init deploys today is 295,899 bytes and locks about 2.96 NEAR. The older 2.0.0 contract, which this site runs, is 216,419 bytes and locks a little over 2 NEAR.
Events are one log line per action, prefixed with EVENT_JSON:, standard quay, with the contract version: 3.0.1 on a new account, 2.0.0 on the older ones.
Limits and caveats
| Chunk | 1 to 200,000 bytes |
|---|---|
| File | 800,000 bytes, at most 4 chunks |
| Files in one release | 64 |
| Live releases | 32 |
| Payload cap | 2,000,000 bytes |
| Message | 200 characters, no control characters |
| Path | 1 to 180 bytes: letters, digits, . _ - / |
This is testnet. The faucet is rate-limited and does not spend mainnet NEAR. There is no mainnet deploy path in the CLI.
The public gateway issues a TLS certificate the first time a new host is requested. That often takes under a minute, and curl can fail the handshake before it succeeds. quay init and the first quay deploy poll the gateway and print "waiting for the gateway certificate, usually under 2 minutes". If that wait times out, the account and the release are already on chain. Open the URL again in a minute.
A public site is only as available as the gateway that serves it. On 2026-11-10 testnet.page answered every account with HTTP 400 for several hours. The contract still held the bytes. Run quay gateway --account <account> and open http://127.0.0.1:8787/, or deploy gateway/worker to your own Cloudflare account. The trust table lists every service a site depends on.
A full-access key can replace the contract wasm. A release is not owner-proof against that key. The publish-only key cannot.
Dotfiles, node_modules, target, and .git are skipped. A directory release needs index.html. About 1 NEAR is locked per 100 KB until you delete the release, so keep the site small. The contract code lock is separate and comes back only if you delete the account.