Files
pezkuwi-telegram-miniapp/package.json
T
pezkuwichain 9c6af146e6 ops: let a function be retired, not just deployed (#17)
* ops: let a function be retired, not just deployed

The guarded deploy could create and update, never remove. rsync runs without
--delete on purpose, so one project cannot wipe another's functions — but the
consequence was that nothing ever left. A function deleted from its repository
kept serving traffic, and nobody would know.

Found the concrete case on 2026-08-01: email-verification had been removed from
pwap on 2026-07-30 and was still live and reachable with only the anon key,
running under the service-role key with Access-Control-Allow-Origin *. Its
verify path sets profiles.email_verified for whatever user_id a token maps to.
It was not exploitable — the send path calls getUserByEmail, which the SDK does
not have, so no tokens could be minted and the table was empty. Broken is not
the same as safe; it was protected by a bug, not by design.

- --audit compares the volume against the registry both directions and exits 1
  on drift. Runs after every deploy too, so a leftover is stated rather than
  invisible.
- --retire moves functions into /opt/supabase-self-hosted/retired-functions/
  with a timestamp. Quarantined, not deleted: retiring the wrong thing has to be
  one command to undo, and that copy is the only remaining record of what was
  serving traffic.
- _cloud_hosted names now count as declared. They live on the cloud project and
  are owned by nobody here, so without this the audit would have offered to
  retire ask and telegram-bot, which look live.

Applied: email-verification retired and dropped from the registry; the audit is
clean; two-factor-auth and the miniapp's check-deposits both still answer.

* ops: catch the function a project still serves but no longer ships

The retire mechanism closed the second half of the problem. This closes the
first: nothing could detect that email-verification needed retiring.

It was never an orphan. The registry listed it as owned and current, the volume
agreed, and both were right about each other — the repository was the one that
had dropped it. Volume-vs-registry compares two of the three and cannot see a
disagreement in the third. It only became visible as drift because I removed it
from the registry by hand, which means the mechanism did not find it; I did.

A deploy is the one moment we hold the project's actual source next to the
registry and the volume, so it is the only place all three can be compared.
Anything the project owns, that is on disk, and that is absent from what it just
shipped, is now named with the command to retire it.

Warns rather than refuses: deleting a function and shipping an unrelated change
in the same commit is ordinary, and blocking that deploy until someone retires
would punish the correct move. It is printed in the deploy output and recorded
as an ABANDONED line in the deploy log.

Verified against a source tree missing eight of pwap-web's functions: all eight
named, deploy still allowed.
2026-08-01 02:30:25 -07:00

92 lines
2.9 KiB
JSON

{
"name": "pezkuwi-telegram-miniapp",
"version": "1.0.246",
"type": "module",
"description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards",
"author": "Pezkuwichain Team",
"license": "MIT",
"scripts": {
"dev": "vite",
"build": "node scripts/bump-version.mjs && tsc && vite build",
"build:major": "node scripts/bump-version.mjs major && tsc && vite build",
"build:minor": "node scripts/bump-version.mjs minor && tsc && vite build",
"build:patch": "node scripts/bump-version.mjs patch && tsc && vite build",
"build:no-bump": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --ext ts,tsx --fix",
"format": "prettier --write \"src/**/*.{ts,tsx,css}\"",
"typecheck": "tsc --noEmit",
"test": "vitest",
"test:run": "vitest run",
"test:unit": "vitest run --grep 'P2P Fiat Trading E2E' --invert",
"test:e2e": "./scripts/run-e2e-tests.sh",
"test:p2p": "vitest run --grep 'P2P'",
"test:coverage": "vitest run --coverage",
"prepare": "husky",
"validate": "npm run typecheck && npm run lint && npm run test:run && npm run build:no-bump",
"version": "node scripts/bump-version.mjs"
},
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,css}": [
"prettier --write"
]
},
"dependencies": {
"@pezkuwi/api": "^16.5.36",
"@pezkuwi/keyring": "^14.0.25",
"@pezkuwi/util-crypto": "^14.0.25",
"@supabase/supabase-js": "^2.93.1",
"@tanstack/react-query": "^5.56.2",
"@ton/crypto": "^3.3.0",
"@ton/ton": "^16.2.2",
"@types/qrcode": "^1.5.6",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"js-sha3": "^0.9.3",
"lucide-react": "^0.462.0",
"qrcode": "^1.5.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"vite-plugin-node-polyfills": "^0.25.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.54.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"@vitest/coverage-v8": "^4.0.18",
"autoprefixer": "^10.4.20",
"bip39": "^3.1.0",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"husky": "^9.1.7",
"jsdom": "^27.4.0",
"lint-staged": "^16.2.7",
"pg": "^8.18.0",
"postcss": "^8.4.47",
"prettier": "^3.8.1",
"tailwindcss": "^3.4.11",
"tronweb": "^6.2.0",
"typescript": "^5.5.3",
"vite": "^5.4.1",
"vitest": "^4.0.18"
},
"engines": {
"node": ">=18.0.0"
}
}