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.

Terminal
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 --help
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:

Terminal
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

Chunk1 to 200,000 bytes
File800,000 bytes, at most 4 chunks
Files in one release64
Live releases32
Payload cap2,000,000 bytes
Message200 characters, no control characters
Path1 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.