Ports and the container runtime
- Container runtime unavailable.
pnpm infra:upexits whendockeris missing or the daemon is stopped. Start the runtime and runpnpm infra:upagain. - Port 5432 is taken. Another PostgreSQL is listening on
127.0.0.1:5432. Stop it, or change the host port incompose.yamlandDATABASE_URLtogether. - Port 3900 is taken. Another process is listening on
127.0.0.1:3900. Stop it, or change the Garage host port incompose.yamlandS3_ENDPOINTtogether. - Port 3000 or 3001 is taken. Stop the other process and start again. Local development uses
http://localhost:3000for the web app andhttp://localhost:3001for the API. pnpm e2ereports a taken port. That command uses127.0.0.1:5433for PostgreSQL,127.0.0.1:3910for Garage,127.0.0.1:3100for the web app, and127.0.0.1:3101for the API.
Startup and storage
- The API stops on
BETTER_AUTH_SECRET. The value is missing or shorter than 32 characters. Set it inapps/api/.env, then start the API again. - The API stops on
EMAIL_TRANSPORT. The value is notlog,smtp, orbrevo. Unset useslog. - The API stops on
EMAIL_FROM_ADDRESSorEMAIL_FROM_NAME. The from address is not an email address, or a set from name is longer than 70 characters or more than one line. An empty address usessupport@factrove.app. An empty name usesFactrove. - The API stops on
SMTP_HOST,SMTP_PORT,SMTP_SECURE,SMTP_USER,SMTP_PASSWORD, orSMTP_REQUIRE_TLS. Those checks run whenEMAIL_TRANSPORTissmtp. Set host, port, andSMTP_SECURE(trueorfalse). SetSMTP_USERandSMTP_PASSWORDtogether, or leave both empty.SMTP_REQUIRE_TLSis optional: unset requires STARTTLS whenSMTP_SECUREisfalse.falseallows plaintext. An empty SMTP block is valid when the transport islog. - The API stops on
BREVO_API_KEY. That key is required only whenEMAIL_TRANSPORTisbrevo. The API does not callGET /v3/account. - The invite is only in the API log.
EMAIL_TRANSPORTis unset orlog, so the log saysEMAIL_TRANSPORT is log. Invite email was not sent.Setsmtporbrevoinapps/api/.envand create an account with a new address. The recipe takes that address as its first argument. See Invite email. - Sign-up returns 200 and the log says the invite email failed. The account exists. The line starts with
Invite email failed for. Check the SMTP server or the Brevo key for the transport you selected. The from address defaults tosupport@factrove.app. A second sign-up for the same address is 422 and does not send again. - Shutdown logs
Invite email shutdown left N send(s) unfinished. The API waited 10 seconds for queued and in-flight invites, then exited. Those accounts exist. A second sign-up for the same address is 422 and does not send again.pnpm devand Compose give the API 15 seconds before a force stop. - The log says
Invite email skipped for. One hundred invites were already waiting. The account exists. That invite was not sent. - Upload returns
STORAGE_UNAVAILABLE. The API could not write the object, and the failed upload leaves no artifact row. For Garage, runpnpm infra:upand matchS3_ENDPOINT,S3_BUCKET,S3_REGION,S3_FORCE_PATH_STYLE, and the access key inapps/api/.envtocompose.yaml. For AWS, setS3_BUCKETandS3_REGION, removeS3_ENDPOINT,S3_FORCE_PATH_STYLE, and the Garage key pair, and use real credentials or the AWS credential chain.S3_BUCKET=your-bucketis an old placeholder. See Object storage. - The CLI reports
NETWORK_ERROR. The API origin is wrong or the API is not running. Precedence is--endpoint-urlor--api-url, thenFACTROVE_API_URL, thenFACTROVE_ENDPOINT_URL, then the selected profile’sapi_urlorendpoint_url, then a URL fromapps/cli/.envwhen you usepnpm factrove, thenhttps://api.factrove.app. An explicit--profileuses that profile’s key and ignoresFACTROVE_API_KEY. The published commands arefactroveandftrv.pnpm factroveis the checkout launcher. - The library says the API is unavailable. The web app is using a different origin from the running API. Set
NEXT_PUBLIC_API_URLinapps/api/.envor the environment, then restartpnpm dev. A production bundle frompnpm buildkeeps the origin from the build, so runpnpm buildagain after changing it. On the Docker web image, setBROWSER_API_URLto the origin the browser uses andINTERNAL_API_URLto the origin the Next.js server can reach. Those two are read at request time. - Docker Compose upload returns
STORAGE_UNAVAILABLE. The API container reaches Garage athttp://garage:3900withS3_ALLOW_INSECURE_HTTP=true.pnpm devdoes not use that flag. It keepshttp://127.0.0.1:3900. docker compose upstarts only PostgreSQL and Garage. The command without-f docker-compose.ymlusescompose.yaml. The self-host stack isdocker compose --env-file .env.docker -f docker-compose.yml up --build -d.- Garage exits during the self-host start. The log says
Invalid RPC secret key: expected 32 bytes of random hex.GARAGE_RPC_SECRETmust be 64 hex characters.openssl rand -hex 32prints that. The placeholder in.env.docker.exampleis not a valid secret. - The API exits and the database URL looks truncated.
POSTGRES_PASSWORDis copied intoDATABASE_URLwithout encoding.@,:,/, or#in that password breaks the URL. Use the hex output ofopenssl rand -hex 32. - Self-host ports 3000 or 3001 are taken.
WEB_PORTandAPI_PORTdefault to those ports, on127.0.0.1.pnpm devuses the same pair. Stop the other process, or changeWEB_PORT,API_PORT, and the public origins together. See Configure.
Cookies and origins
A cookie-authenticated write (POST, PUT, DELETE, and other methods besides GET, HEAD, and OPTIONS) needs Origin or Referer to be WEB_ORIGIN or PUBLIC_BASE_URL. A missing or different origin is 403 FORBIDDEN with message This request origin is not allowed. Bearer requests skip that check. Do not send Authorization on API-key create, list, or delete.
localhost and 127.0.0.1 are different hosts. A cookie domain cannot cover either. When the web app and API use different public hosts, set AUTH_COOKIE_DOMAIN to a parent of both. On an https API origin the session cookie is named __Secure-better-auth.session_token. On http it is better-auth.session_token. Use a cookie jar rather than typing the name. The recipe is in API overview.
Profiles
--profile, then FACTROVE_PROFILE, then default selects the profile. pnpm factrove can fill an unset FACTROVE_PROFILE from apps/cli/.env. node apps/cli/dist/main.js does not read that file. Profile files live in ~/.factrove unless FACTROVE_CONFIG_DIR, FACTROVE_SHARED_CREDENTIALS_FILE, or FACTROVE_CONFIG_FILE points elsewhere.
Public links
Revoke clears the password. The next share is unprotected unless you pass the password again:--password needs a terminal. Rotating SHARE_TOKEN_KEY, or BETTER_AUTH_SECRET when that key is unset, makes existing public URLs return 404. Share again and supply the password if the link should stay protected. See Lockout recovery.
Content tickets
A signed-in viewer loads HTML through a shortaccess query on the content URL. That grant lasts five minutes. Refresh the viewer to mint another one. A password-protected public link uses a separate ticket query that lasts 60 seconds and is honored at most three times per API process. Refresh the share page to mint another ticket. Reverse proxies should not log either query.