Use the Better Auth session cookie created by Google sign-in.
Quick,
documented.
Publish a static directory, keep ownership and deployment history, and give Codex a safe operational workflow.
Quickstart
Create a personal API key in the authenticated dashboard, configure the CLI and deploy a static directory.
quick init --api-url https://quick.mcamprodon.cat --token "quick_..."
quick doctor
quick deploy ./dist --site field-notesThe published site will be available at https://sites.mcamprodon.cat/field-notes/.
The API origin is derived automatically from VERCEL_PROJECT_PRODUCTION_URL.
Authentication
The web application uses Google OAuth through Better Auth. By default, new accounts and existing sessions must use an exact domain from AUTH_ALLOWED_DOMAINS. Set AUTH_ALLOWED_EMAILS to restrict access to a comma-separated list of exact addresses instead.
Use a personal, revocable API key in the Bearer header.
Authorization: Bearer quick_...
# Environment variables override ~/.quick/config.json
QUICK_API_URL=https://quick.mcamprodon.cat
QUICK_API_TOKEN=quick_...
# Server-side authentication policy
AUTH_ALLOWED_DOMAINS=ara.cat
AUTH_ALLOWED_EMAILS=alice@ara.cat,bob@ara.catNever use Google OAuth tokens or browser cookies in the CLI. Create and rotate API keys from the dashboard.
Production database
Quick falls back to SQLite locally. Set DATABASE_URL to use any PostgreSQL-compatible service in production, including Neon, and run the migrations before serving traffic.
DATABASE_URL=postgresql://user:password@host/database?sslmode=require
DATABASE_POOL_MAX=5
npm run auth:migratePrefer a pooled PostgreSQL connection string in serverless environments. DATABASE_URL takes precedence over AUTH_DATABASE_PATH; migration from existing SQLite data is not automatic.
CLI reference
quick init --api-url <url> --token <key>Store API configuration in ~/.quick/config.json.quick --json doctorVerify configuration, authentication and S3 reachability.quick --json deploy ./dist --site <slug> --dry-runInventory and validate without publishing.quick --json deploy ./dist --site <slug>Upload and publish the directory.quick --json deployments get <slug>Read the current manifest.quick --json request get /api/sitesUse the authenticated read-only escape hatch.With --json, successful commands return {"ok":true,...}. Errors use {"ok":false,"error":{"code","message"}} and a non-zero exit code.
HTTP API
/api/healthCheck authentication, configuration and S3 reachability./api/deploymentsReserve a site and create presigned upload URLs./api/deployments/:id/completeValidate uploaded objects and publish the manifest./api/sitesList sites owned by the authenticated user./api/sites/:siteRead the currently published deployment manifest./api/sites/:site/deploymentsRead deployment history for a site./api/sites/:siteDelete the S3 prefix, site and deployment history.Create an upload plan
POST https://quick.mcamprodon.cat/api/deployments
Authorization: Bearer quick_...
Content-Type: application/json
{
"site": "field-notes",
"files": [{
"path": "index.html",
"size": 1532,
"contentType": "text/html",
"sha256": "<64 lowercase hex characters>"
}]
}The response contains a deployment ID, one presigned S3 PUT URL per file, required headers and a relative completeUrl. Upload bytes directly to S3, then submit the same manifest to the completion endpoint.
Error codes
401 unauthorizedMissing or invalid session/API key.409 site_ownedThe slug belongs to another user.409 deployment_in_progressThe site already has a pending upload.409 manifest_mismatchThe completion body differs from the upload plan.413 file_limitToo many files or a file exceeds the configured size.Codex skill
The companion skill teaches Codex to verify configuration, run a dry-run first, deploy only when requested and avoid exposing credentials or signed URLs.
Install from this repository
mkdir -p ~/.codex/skills
ln -s /absolute/path/to/quick/skills/quick-deploy \
~/.codex/skills/quick-deployRestart Codex after installation. Invoke it explicitly with:
$quick-deploy publish ./dist as field-notesRuns doctor and --dry-run before unfamiliar deployments.
Treats site_owned as a slug conflict, never as something to bypass.
Uses personal API keys and never prints config, OAuth tokens or presigned URLs.
AWS infrastructure
This setup keeps S3 private, lets Quick upload through presigned URLs, and serves sites through CloudFront. Viewer access is an infrastructure decision: the distribution can be public or protected independently of Quick.
1. Create the S3 bucket
- Create
quick-artifacts-mcamprodonineu-central-1. - Keep Block Public Access enabled and Object Ownership set to Bucket owner enforced.
- Do not enable S3 static website hosting. CloudFront must use the regular S3 bucket endpoint.
- Quick stores objects under
sites/<site-slug>/. The CLI uploads to presigned URLs, so bucket CORS is not required for this flow.
2. Give Quick access to the bucket
Attach this identity policy to the IAM user or role used by the Next.js application. Keep the resource restricted to the bucket and the sites/* prefix.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "InspectQuickBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::quick-artifacts-mcamprodon"
},
{
"Sid": "ManageQuickSites",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::quick-artifacts-mcamprodon/sites/*"
}
]
}3. Create CloudFront with Origin Access Control
- Create a distribution with the S3 origin
quick-artifacts-mcamprodon.s3.eu-central-1.amazonaws.com. - Set the origin path to
/sites. A viewer request for/demo/will then readsites/demo/index.htmlfrom S3. - Create an Origin Access Control for S3, select Sign requests, and use SigV4. Attach it to the origin.
- Use Redirect HTTP to HTTPS, allow
GET/HEAD, and start with the managedCachingOptimizedpolicy.
Add this bucket policy after replacing the account and distribution IDs:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowCloudFrontOACRead",
"Effect": "Allow",
"Principal": { "Service": "cloudfront.amazonaws.com" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::quick-artifacts-mcamprodon/sites/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::<AWS_ACCOUNT_ID>:distribution/<DISTRIBUTION_ID>"
}
}
}]
}4. Configure the sites hostname
- Request or import an ACM certificate in
us-east-1for the sites hostname, for examplehttps://sites.mcamprodon.cat. - Add that hostname as an alternate domain name on the distribution and select the certificate.
- Create a Route 53 alias
A/AAAArecord, or a CNAME with another DNS provider, pointing to CloudFront. - The Quick application can use a separate hostname such as
https://quick.mcamprodon.cat; it does not need to share cookies or authentication with the sites hostname.
5. Add the viewer-request Function
Create a CloudFront Function using JavaScript runtime 2.0. Copy infrastructure/cloudfront/viewer-request.js, publish it, and associate it with Viewer request on the sites behavior.
function handler(event) {
var request = event.request;
if (request.uri.endsWith("/")) request.uri += "index.html";
else if (!request.uri.includes(".")) request.uri += "/index.html";
return request;
}This Function only rewrites directory-like URLs to index.html. It does not implement authentication or authorization.
6. Configure Quick
BETTER_AUTH_URL=https://quick.mcamprodon.cat
QUICK_PUBLIC_BASE_URL=https://sites.mcamprodon.catQuick uses QUICK_PUBLIC_BASE_URL to build links shown by the API, CLI and dashboard. It does not assume whether those URLs are public or protected.
Access policy
Leave viewer access unrestricted and let CloudFront serve the static artifacts directly.
Add the access mechanism required by your organization at CloudFront, a proxy, VPN or another gateway. Quick remains unchanged.
Verification checklist
Direct S3 URLReturns AccessDenied.CloudFront site URLServes the deployed artifact according to the distribution access policy.Nested routeRewrites to the corresponding index.html.Quick dashboardOpens the configured public URL directly.AWS references: S3 origins and OAC, CloudFront Functions, and alternate domain names.
Current constraints
- Static files only; conventional sites should include
index.html. - Site slugs are globally unique and owned by their first authenticated publisher.
- Only one deployment can be pending per site.
- Deployments overwrite matching paths but do not delete stale unmatched files.
- SQLite is available for local development; production deployments should configure PostgreSQL with
DATABASE_URL. - The bucket or CDN must serve objects under
sites/<site>/. - Quick does not control viewer access to published sites; configure public or restricted access in the chosen CDN or hosting layer.