跳到正文
cccskills
免费在 GitHub 公开

secrets-ops

Handling secrets, keys and credentials for site-os — reading vigyan-secrets.md safely, moving keys into app_secrets, rotating, and responding to an exposure. Use whenever a task touches a key, token, password, .env file, app_secrets, encryption, or the secrets document.

中文简介正在准备中。当前显示原文说明。

集成工具Vercel
查看安装方法

包含的文件(1)

  • SKILL.md5.8 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Secrets operations

The canonical record is vigyan-secrets.md in Nextcloud (admin/files/YourSite/), section 0 of which is the registry: which key is which, who issues it, how to reissue it. Procedures live in docs/SECURITY-OPS.md. Neither is duplicated into this repository.

Rule 1 — how to read the secrets file

By heading, or by exact key name. Never a line range.

sudo grep -nE '^#{1,4} ' <file>          # headings — safe
sudo grep -n 'RESEND_API_KEY' <file>     # one key by name — safe
sudo sed -n '340,360p' <file>            # NEVER

On 2026-09-29 a sed range with a length-based redact (>=20 chars) echoed Nextcloud TOTP backup codes — which are 16 characters — into a session transcript. Length-based filtering does not work. Neither does "I'll be careful."

When you need to know whether two secrets are the same, fingerprint, never print:

sha256sum <<< "$VALUE" | cut -c1-12      # comparable, reveals nothing

Rule 2 — where a key belongs

Can the app read it before it can reach the database? Yes → Coolify env. No → app_secrets.

Boot-critical, never movable: DATABASE_URL, NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, CONFIG_ENCRYPTION_KEY, ACCOUNT_DELETION_ENCRYPTION_KEY, CRON_SECRET.

CONFIG_ENCRYPTION_KEY decrypts app_secrets, so it can never live inside it.

Everything else — vendor keys, webhook URLs, R2 — belongs in app_secrets and is changeable without a redeploy.

Moving keys into app_secrets

Use the script. Do not hand-roll the encryption; the format has to match src/lib/app-secrets.ts byte for byte or the row is silently undecryptable.

node scripts/seed-app-secrets.mjs --dry-run
node scripts/seed-app-secrets.mjs --only RESEND_API_KEY --dry-run
node scripts/seed-app-secrets.mjs --sql-out /tmp/seed.sql   # then apply, then shred

It prints key names and fingerprints only, refuses boot-critical keys, refuses keys outside the MANAGED list, verifies every round-trip before emitting SQL, and refuses to print SQL to stdout.

Always compare CONFIG_ENCRYPTION_KEY fingerprints first. The script prints its own; compare with production's. A mismatch means rows write successfully and cannot be decrypted there.

Verify after applying by decrypting with production's key, not by trusting the insert. Format is base64(iv[12] ‖ authTag[16] ‖ ciphertext), optionally prefixed v<N>:. Note Node puts the auth tag in the middle; Python's AESGCM expects it appended — reorder as body + tag or you get a spurious InvalidTag.

Rollback is DELETE FROM app_secrets WHERE key = '...' — getSecret() falls straight back to process.env.

Rotation

Overlap, always: create the new credential, deploy it, verify, then revoke the old one. Never revoke first.

CONFIG_ENCRYPTION_KEY is special — add CONFIG_ENCRYPTION_KEY_V2, set CONFIG_ENCRYPTION_KEY_CURRENT=2, keep V1 until no v1 rows remain.

If a value is exposed

  1. Rotate first, investigate second. Everything is cheap to rotate except the age backup private key, which cannot be rotated at all for existing backups.
  2. Scrub what you can and say plainly what you cannot. Transcripts are at ~/.claude/projects/*/*.jsonl; git history is permanent.
  3. Record it in vigyan-secrets.md under the affected key, with the date.

Write credentials to production — treat an exposure of these as urgent: SUPABASE_SERVICE_ROLE_KEY, MCP_SECRET_KEY, WEBHOOK_SECRET (n8n creates posts through /api/webhook/blog with it), the GitHub PAT.

What this codebase does NOT encrypt

mask_email() is a plain SQL string function with no key. Masking is access control — capabilities plus security_barrier views — and the underlying value is plaintext. A pg_dump sees the real email. That is why backup encryption is a separate control and why the age keypair exists.

Never

  • Paste a value into a chat, a commit, or a terminal that logs
  • Put anything sensitive behind NEXT_PUBLIC_ — it ships in the browser bundle
  • Copy a broad-scoped credential onto a server that only needs a narrow one
  • Write key documentation into this repo; it goes in vigyan-secrets.md

Bootstrap values: vault → platform (SOPS + age, 2026-10-02)

env.manifest.json names every variable, its tier and purpose (no values). Values live in the encrypted vault secrets/<env>.sops.env; the age private key lives only in ~/.config/sops/age/keys.txt (never commit, back up offline).

TierWhere the value goes
bootstrap / publicPlatform env, pushed from the vault (pnpm secrets sync coolify|vercel --env prod --apply)
secretAdmin → Settings → Secrets (encrypted app_secrets); vault copy optional
configAdmin → Settings (app_config)
localOperator shell / local scripts only
deprecatedRemove everywhere

Rules for agents:

  • Never print, echo or log a value. The tool prints names, counts and plans only.
  • pnpm secrets check --env prod before any sync; fix "missing required" and "not in manifest" first. pnpm env:check fails if code reads a name the manifest does not describe.
  • Platform syncs are dry runs unless --apply. Build-time (phase: build) vars need a redeploy to take effect.
  • New variable = add it to env.manifest.json in the same change, with a tier.
  • Prove a pushed value works by behaviour (the integration answers), not by reading it back.
  • Moving hosts (Coolify → Vercel → AWS) changes only the bootstrap tier, ~7 vars.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

brand

免费

Apply this site's visual identity — colours, type, logo files and fixed brand strings. Use for any page, component, social asset or document that should look like the site.

中文简介正在准备中。当前显示原文说明。

aryannema/vigyan-site-os22026年10月2日 更新

Use FIRST when someone asks for a new feature or a change to an existing one — before any schema, code or test. Works out what is actually being asked for, whether to build it or integrate a third party (Trustpilot, Stripe, Calendly and the like), where the data lives, who may do what, and what happens when the vendor is down. Produces a decision, then hands to feature-schema.

中文简介正在准备中。当前显示原文说明。

aryannema/vigyan-site-os22026年10月2日 更新

Use when a new feature needs a table, column or capability — evolving the schema so the new thing joins the permission, audit and RLS contract instead of sitting outside it. Covers the DDL/DML split, the resource/capability wiring, and the self-verifying migration.

中文简介正在准备中。当前显示原文说明。

aryannema/vigyan-site-os22026年10月2日 更新

Use after building a feature — explore it in a real browser to find what nobody predicted, then turn each finding into a deterministic test that guards it forever. Covers the agentic pass, what to generate vs hand-write, and how to skip honestly when a capability is missing.

中文简介正在准备中。当前显示原文说明。

aryannema/vigyan-site-os22026年10月2日 更新

Publishing content or deploying site-os — what caches exist, what refreshes them, and how to prove a change is actually live. Use when publishing a post or product, launching a page, deploying, or when someone says a change "isn't showing".

中文简介正在准备中。当前显示原文说明。

aryannema/vigyan-site-os22026年10月2日 更新

Audit a page or the whole yoursite site (or a new site) for indexing readiness — cache headers, sitemap, robots, canonical, JSON-LD, internal links — and check the publish pipeline (Cloudflare purge/warm, IndexNow, GSC, Bing). Use before/after publishing content or launching a site.

中文简介正在准备中。当前显示原文说明。

aryannema/vigyan-site-os22026年10月2日 更新

查看 aryannema 的全部技能

报告此技能的问题