Cryptographically reinforced form builder that utilizes ML-KEM-1024, as well as the "ChaCha20 + Serpent-256 CBC + HMAC-SHA3-512" authenticated encryption scheme to enable end-to-end encryption for enhanced data protection.
Check it out at https://blueberry-loom.netlify.app/
You can set a password that contains non-ASCII characters.
The article about how this web app was built and how it works—including the part that covers the cryptographic mechanisms ensuring only the intended recipient is capable of decrypting a submitted response, and explaining how it came to be that way—is available at https://medium.com/@Northstrix/creating-a-form-builder-powered-by-advanced-cryptography-d1e827a6ddd5
SourceForge page with the version of the app where forms can be accessed using dedicated links instead of tags https://sourceforge.net/projects/blueberry-loom/
The app is localized into the following languages:
- English
- Hebrew (some inscriptions Latinized)
- Latin American Spanish
- German
- French
- Italian
- Brazilian Portuguese
- Polish
- Cantonese
I assume you already have Node.js and npm installed.
-
Clone the repository using the command:
git clone https://github.com/Northstrix/blueberry-loom -
Open the project:
- Open the cloned folder in VS Code or any IDE of your choice.
-
Configure Firebase:
- Open
app/lib/firebase.tsfile. - Create a Firebase instance with Authentication and Firestore Database enabled.
- Create a new web app in the Firebase instance.
- Replace the mock credentials in
firebase.tswith your actual Firebase credentials and save the file.
- Open
-
Set Firestore rules:
-
Apply the following rules to your Firestore database:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // === 1. /data/{userEmail}/forms/{formId} === match /data/{userEmail}/forms/{formId} { // Allow anyone to increment visits or responses by +1, but only if form is public allow update: if ( resource.data.isPublic == true && ( (request.resource.data.diff(resource.data).affectedKeys().hasOnly(['visits']) && request.resource.data.visits == resource.data.visits + 1) || (request.resource.data.diff(resource.data).affectedKeys().hasOnly(['responses']) && request.resource.data.responses == resource.data.responses + 1) ) ); // Allow get/list if public, or if the user is the owner and email is verified allow get, list: if resource.data.isPublic == true || (request.auth != null && request.auth.token.email == userEmail && request.auth.token.email_verified == true); // Allow create, update, delete only if the user is the owner and email is verified allow create, update, delete: if request.auth != null && request.auth.token.email == userEmail && request.auth.token.email_verified == true; } // === 1b. /data/{userEmail}/forms/{formId}/{document=**} === match /data/{userEmail}/forms/{formId}/{document=**} { // Owner (with verified email) can read/write/delete allow read, write, delete: if request.auth != null && request.auth.token.email == userEmail && request.auth.token.email_verified == true; // Anyone can get/list if parent form is public allow get, list: if exists(/databases/$(database)/documents/data/$(userEmail)/forms/$(formId)) && get(/databases/$(database)/documents/data/$(userEmail)/forms/$(formId)).data.isPublic == true; } // === 2. /data/{userEmail}/receivedResponses/{document=**} === match /data/{userEmail}/receivedResponses/{document=**} { allow write: if true; // Anyone (including non-authenticated users) can write allow read, delete: if request.auth != null && request.auth.token.email == userEmail; // Only owner can read/delete } // === 2b. /data/{userEmail}/receivedBackups/{document=**} === match /data/{userEmail}/receivedBackups/{document=**} { allow write: if true; // Anyone (including non-authenticated users) can write allow read, delete: if request.auth != null && request.auth.token.email == userEmail; // Only owner can read/delete } // === 3. /data/{userEmail}/public/{document=**} === match /data/{userEmail}/public/{document=**} { allow read: if true; // Anyone can read allow write, delete: if request.auth != null && request.auth.token.email == userEmail; // Only owner can write/delete } // === 4. /data/{userEmail}/private/encrypted/formData/all/keys/{uniqueFormId} === match /data/{userEmail}/private/encrypted/formData/all/keys/{uniqueFormId} { // Only owner with verified email can write/read allow create, read, update, delete: if request.auth != null && request.auth.token.email == userEmail && request.auth.token.email_verified == true; } // === 5. /data/{userEmail}/private/{document=**} === match /data/{userEmail}/private/{document=**} { // Any authenticated user can read/write/delete their own private route (regardless of email verification) allow read, write, delete: if request.auth != null && request.auth.token.email == userEmail; } // === 6. /data/{userEmail}/private root === match /data/{userEmail}/private { allow read, write, delete: if request.auth != null && request.auth.token.email == userEmail; } // === 7. Default deny === match /{document=**} { allow read, write, delete: if false; } } }
-
-
Install dependencies by running:
npm install -
Start the development server with:
npm run dev -
If you want to have the ability to share the forms by distributing their links - Set up the form loader
7.1. Repalce the form loader base URL within this app with your own (Ctrl + Shift + F -> https://blueberry-loom-form-loader.netlify.app)
Text Rotate by fancy components
Input Floating Label animation by Elpeeda
チェックしないと押せないボタン by あしざわ - Webクリエイター
Help Button by LN
Dot Loader by PaceUI
Parallax Floating by fancy components
Glowing Effect by Aceternity UI
Card Spotlight by Aceternity UI
Canvas Reveal Effect by Aceternity UI
Fey.com Macbook Scroll by Aceternity UI
Animated Tooltip by Aceternity UI
React Wheel Picker by Chánh Đại






