CC Safety Net
A coding agent CLI safety layer that blocks destructive git and filesystem commands and secret file access before execution.
Policy enforcement for teams running AI agents in production. A self-hosted gate between your agents and the tools they call: deny-by-default argument contracts, payload-bound approvals, reconcile-bef
SecurityJavaScript · HTMLGitHub @rohansxOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Policy enforcement for teams running AI agents in production. A self-hosted gate between your agents and the tools they call: deny-by-default argument contracts, payload-bound approvals, reconcile-before-retry and a hash-chained decision log you can verify offline. Model proposes. Code disposes.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
The CloakPipe marketing site and docs (cloakpipe.co): a static
Astro site, deployed by Vercel on every push to main.Read the full README ↗The CloakPipe marketing site and docs (cloakpipe.co): a static
Astro site, deployed by Vercel on every push to main.
npm ci
npm run dev # http://localhost:4321
npm run build # static site in dist/
node scripts/check-links.mjs # every internal link in dist/ must resolve
npm test # unit tests: waitlist function, Apps Script, site content rules (node:test)
npm run test:site # build + link check
src/pages/ (docs are Markdown in src/pages/docs/, sidebar order in src/lib/docs.ts).src/partials/*.html; shared chrome: src/components/, src/layouts/.vercel.json./docs/*, /blog, /changelog and /waitlist. The old pricing, platform, trust,
integrations, playground and dashboard pages are gone; vercel.json redirects their URLs..github/workflows/ci.yml) runs npm test and npm run test:site on every pull request.
test/site-content.test.js also fails on retired positioning phrases.src/pages/waitlist.astro, Vercel Function api/waitlist.js (logic in api/_lib/, tests in
test/), Google Apps Script apps-script/Code.gs.The product itself is open source at rohansx/cloakpipe.
The primary call to action is the open-source quick start (/docs/quickstart). /waitlist is secondary: it
is "Hosted alerts & approvals (Team): tell us you want it", the count of teams that want a hosted plane,
de-duplicated by organisation, so company and a "wants hosted alerts/approvals" checkbox are required. The form
posts to the Vercel Function POST /api/waitlist (api/waitlist.js), which checks the request (method, origin,
rate limit, honeypot, timing, field validation and limits, formula neutralisation) and forwards the clean row
to a Google Apps Script web app bound to the sheet (apps-script/Code.gs). The
script runs as you, checks a shared secret, de-duplicates by email under a lock and appends the row. There is no
Google Cloud project, service account or key.
Until the two env vars below are set, the function answers 503 and the form shows a "temporarily unavailable"
message with a mailto fallback.
openssl rand -hex 32
This prints 64 hex characters. You paste the same value into the script (step 3) and into Vercel (step 5). The function refuses secrets shorter than 32 characters. Keep it out of the repository and out of chat logs.
Code.gs file bound to this sheet.function myFunction() {}, paste the whole of
apps-script/Code.gs, and press Save project (the disk icon, or ⌘S / Ctrl+S).
Optionally rename the project (click "Untitled project") to "CloakPipe waitlist".In the Apps Script editor, click Project Settings (the gear icon in the left sidebar).
Scroll to Script properties → Add script property (or Edit script properties if some exist).
Add:
| Property | Value |
|---|---|
WAITLIST_SECRET | the secret from step 1 |
WAITLIST_TAB | optional tab name; default Waitlist |
Save script properties.
The script creates the tab if it is missing and writes the header row when the tab is empty:
| A | B | C | D | E | F | G | H | I | J | K | L |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Timestamp (UTC) | Name | Company | Role | Use case | Source page | UTM source | UTM medium | UTM campaign | User agent | Wants hosted alerts/approvals |
New columns are only ever appended. On a sheet created before column L existed, type the L1 header by hand and redeploy the script (a new version of the same deployment) so it writes the column.
Email must stay in column B: it is read for de-duplication (case-insensitive). IP addresses are never stored;
"User agent" is a short family such as Chrome/macOS. Values starting with = + - @ are prefixed with ' by
the function and again by the script, so nothing is evaluated as a formula.
waitlist v1. Execute as: Me (your account). Who has access: Anyone.
"Anyone" is required: Vercel calls it without a Google login. The shared secret is what keeps others out.https://script.google.com/macros/s/AKfy…/exec. Use the /exec URL,
not the /dev test URL.Check it answers (this runs doGet, which returns no data):
curl -sL "https://script.google.com/macros/s/AKfy…/exec"
# {"ok":true}
If you get an HTML Google sign-in page instead, "Who has access" is not "Anyone".
Set these for Production and Preview (Project → Settings → Environment Variables), then redeploy:
| Name | Value |
|---|---|
WAITLIST_SCRIPT_URL | the web app /exec URL from step 4 |
WAITLIST_SCRIPT_SECRET | the secret from step 1 (same as the WAITLIST_SECRET script property) |
With the CLI (each command prompts for the value, so it never lands in shell history):
vercel env add WAITLIST_SCRIPT_URL production
vercel env add WAITLIST_SCRIPT_URL preview
vercel env add WAITLIST_SCRIPT_SECRET production
vercel env add WAITLIST_SCRIPT_SECRET preview
Never put the secret in vercel.json, the repository, or a committed .env file. To rotate it, change the
script property and the Vercel variable together, then redeploy the site.
After editing apps-script/Code.gs, paste it into the editor and save, then Deploy → Manage deployments,
select the deployment, click the pencil (Edit), set Version: New version, and Deploy. The /exec URL
stays the same, so Vercel needs no change. (Saving alone does not update a deployment, and New deployment
would create a second URL.) Script property changes take effect immediately, without redeploying.
Unit tests: npm test. They cover validation, the origin check, the rate limit, the Apps Script client (the
302 to script.googleusercontent.com, sign-in pages, HTML and non-JSON answers, timeouts) and Code.gs
itself, run in a node:vm sandbox against fake Sheets, Lock and Properties services.
The script directly. curl -L follows Google's 302 to script.googleusercontent.com and, as curl does for a
302, turns the POST into a GET there, which is exactly what is needed (don't add -X POST, which would make
curl re-POST to the redirect target):
SECRET=… # the shared secret
curl -sL "https://script.google.com/macros/s/AKfy…/exec" \
-H 'content-type: application/json' \
--data "{\"secret\":\"$SECRET\",\"entry\":{\"email\":\"[email protected]\",\"name\":\"curl test\"}}"
# {"status":"joined"}, then {"status":"already"} the second time; {"error":"unauthorized"} with a wrong secret
The site function, after deploying with the env vars set (the Origin header is required):
curl -si https://cloakpipe.co/api/waitlist \
-H 'origin: https://cloakpipe.co' -H 'content-type: application/json' -H 'accept: application/json' \
--data '{"email":"[email protected]","company":"Test","wants_hosted":"yes","role":"Other"}'
# HTTP/2 201 … {"status":"joined"}
curl -i https://cloakpipe.co/api/waitlist answers 405; a POST from another origin answers 403. Preview
deployments may sit behind Vercel Authentication, in which case curl gets Vercel's login page.
In a browser: open /waitlist, submit your own email and check a row appears; submit it again and you should
see "You're already on the list" with no second row. Without JS (disable it in devtools) the form still posts
and the page shows the result. Delete test rows from the sheet afterwards.
On failure the Vercel function log names the step and a hint, e.g. Apps Script redirect failed (HTTP 302): redirected to Google sign-in (hint: the web app is not public: …) or script answered error "unauthorized" (hint: WAITLIST_SCRIPT_SECRET in Vercel does not match …). Script-side errors are under Executions in
the Apps Script editor.
On success the page sends an Umami event waitlist_joined (or waitlist_already) with the selected role
only; email, name and company are never sent. After a no-JS post the event is sent when the page loads with
?joined=… (without a role).
POST /api/waitlist, body application/json or application/x-www-form-urlencoded:
| Field | Rules |
|---|---|
email | required, trimmed and lowercased, max 254, must look like an address |
company | required (company or organisation), max 120 |
wants_hosted | required: yes, on or true (the checkbox) |
name | optional, max 120 |
role | optional, one of Engineering/ML, Platform/SRE, Security/Compliance, Founder/Exec, Other |
use_case | optional, max 1000 |
cp_hp | honeypot, must be empty (a filled one gets a fake success and nothing is stored) |
started_at, submitted_at | epoch ms set by the page's JS; submissions under 2 s are refused. Absent for no-JS posts. |
source, utm_source, utm_medium, utm_campaign | optional; fall back to the same-origin Referer |
Requests must come from cloakpipe.co, www.cloakpipe.co, the deployment's own *.vercel.app URLs, or
localhost outside production. Each warm instance allows about 5 requests per minute per IP.
| Outcome | JSON (Accept: application/json or a JSON body) | Form post (no JS) |
|---|---|---|
| Added | 201 {"status":"joined"} | 303 /waitlist?joined=1#wl-joined |
| Already on the list | 200 {"status":"already"} | 303 /waitlist?joined=already#wl-already |
| Invalid input | 400 {"error":"invalid_email"|"too_long"|"invalid_role"|"company_required"|"hosted_required"|"too_fast"|"invalid"|"invalid_timing", "field"?, "message"} | 303 /waitlist?error=<code>#wl-error |
| Wrong origin | 403 {"error":"forbidden"} | same, ?error=forbidden |
| Rate limited | 429 {"error":"rate_limited"} + Retry-After: 60 | same, ?error=rate_limited |
| Bad content type / too large | 415 / 413 | same |
| Not configured | 503 {"error":"unavailable"} | ?error=unavailable |
| Apps Script error (bad secret, sign-in page, HTML, non-JSON) | 502 {"error":"upstream"} | ?error=server |
| Apps Script timeout (10 s) | 504 {"error":"upstream"} | ?error=server |
| Any other method | 405, Allow: POST |
Error responses never include the secret, the script URL, Google's response bodies or stack traces; the server log names the failing step, the HTTP status, the script's short error code and a setup hint.