A fast Progressive Web App (PWA) for modern notaries: offline support, local encryption, ID scanning, signatures, and print-ready journal PDFs.
Warning
STRICT NON-COMMERCIAL LICENSE This software is 100% free to deploy for personal use but may NOT be sold, monetized, or used for commercial purposes under any circumstances. See LICENSE for full terms governed by Oklahoma law. Unauthorized commercial use carries liquidated damages of $50,000+ per violation.
Get the app running on your computer in under 2 minutes.
Prerequisites: You need Git and pnpm installed. pnpm is a fast package manager — install it with npm install -g pnpm if you have Node.js, or follow the pnpm install guide.
# 1. Clone the repository
git clone https://github.com/SillyHippy/Notary-log
# 2. Enter the project directory
cd Notary-log
# 3. Install all dependencies (downloads the libraries the app needs)
pnpm install
# 4. Build the app (compiles the source code into optimized production files)
pnpm run build
# 5. Start the development server
cd artifacts/notary-journal && pnpm run devOpen your browser to http://localhost:5173. You should see the Notary Journal login screen (PIN setup). If you do, it's working.
Choose one option below. Each one produces a live URL you can open from any device.
| Option | Best For | Server Backup | Difficulty |
|---|---|---|---|
| Zo Computer | All-in-one hosting + backup | Yes (built-in) | Easy |
| Cloudflare Workers | Fast global CDN, free tier | No | Medium |
| Netlify Drag-and-Drop | Quick manual deploy, no git | No | Easy |
| Netlify Git-Connected | Auto-deploy on every commit | No | Easy |
| Cloudflare Pages | Git-connected with _redirects |
No | Medium |
| Hostinger / Shared Hosting | Traditional web hosting | No | Easy |
Important: Only Zo Computer includes the server-side backup API (/api/backup) and multi-user Zo intake (/api/intake with SQLite). Cloudflare, Netlify, and static hosts use Web3Forms for client intake only.
Zo gives you a free computer with 100GB storage, one hosted service, and built-in backup. This is the easiest path because the backup API runs in the same service.
What you'll end up with:
- Public app URL:
https://notary-log-{your-handle}.zocomputer.io - Backup API:
https://notary-log-{your-handle}.zocomputer.io/api/backup - Client intake: Zo token link or Web3Forms fallback (same service)
- Backups stored on the server in
Documents/Notary Journal/backups/ - Optional: Google Drive backup in Settings (only if configured before
bun run build— see below)
Google Drive uses VITE_GOOGLE_CLIENT_ID. That value is baked into the app during bun run build, not read when the server starts.
| Where you set it | Enables Google Drive? |
|---|---|
Zo Advanced → environment variables before build, and written to artifacts/notary-journal/.env (or exported in the shell) before bun run build |
Yes |
| Zo Advanced only, after deploy, with no rebuild | No — Settings will show “Contact the app administrator” |
| Skipped at deploy | No — use the Enable Google Drive later prompt below when ready |
You can use Zo backup, Google Drive, or both. Zo intake and backup are unrelated to this step.
If you use Google OAuth, add your Zo app URL (e.g. https://notary-log-{your-handle}.zocomputer.io) under Authorized JavaScript origins in Google Cloud Console.
cd /home/workspace
git clone https://github.com/SillyHippy/Notary-log
cd Notary-logbun i
bun run buildIf Notary-log is already on this Zo Computer, paste this in Zo AI chat first:
Remove the Notary Journal deployment from this Zo Computer.
1. list_user_services — find any service with label "notary-log" (or entrypoint containing "Notary-log" or "server.ts" for notary).
2. For each match: delete_user_service(service_id=<id>)
3. list_user_services again — confirm notary-log is gone.
4. Do NOT delete Documents/Notary Journal unless I explicitly say "delete all notary data".
5. Report which service_id was deleted.
In your Zo AI chat, paste this entire prompt (one block). Replace {my-handle} with your Zo handle (or ask Zo to use your real handle in URLs):
Deploy Notary Journal on this Zo Computer from scratch. Follow every step in order.
REPO: /home/workspace/Notary-log
GITHUB: https://github.com/SillyHippy/Notary-log
SERVICE LABEL: notary-log
A. PREP
1. cd /home/workspace
2. If Notary-log exists: cd Notary-log && git pull
Else: git clone https://github.com/SillyHippy/Notary-log && cd Notary-log
3. pwd must be /home/workspace/Notary-log
B. BUILD (read Google note in README before this section)
If I want Google Drive backup on this deploy:
- Read VITE_GOOGLE_CLIENT_ID from Zo Advanced environment variables (or ask me for the value).
- Write artifacts/notary-journal/.env containing exactly:
VITE_GOOGLE_CLIENT_ID=<that value>
- Zo Advanced env alone does NOT enable Google Drive — it must be present for bun run build.
If I do NOT want Google Drive yet, skip the .env file and continue; I can enable it later with the README “Enable Google Drive later” prompt.
4. (Google Drive only) create or update artifacts/notary-journal/.env with VITE_GOOGLE_CLIENT_ID
5. bun i
6. bun run build — if this fails, stop and report the error
C. REMOVE OLD SERVICE
7. list_user_services
8. If label "notary-log" exists: delete_user_service(service_id=...)
9. Confirm notary-log is gone
D. REGISTER ONE SERVICE (do NOT set local_port)
10. register_user_service(
label="notary-log",
mode="http",
entrypoint="bun run server.ts",
workdir="/home/workspace/Notary-log",
public=true
)
E. VERIFY
11. Wait 15 seconds
12. cat /dev/shm/notary-log.log | tail -60
Must include: "listening on", "Zo Backup Key", "Zo Intake Token"
13. curl -s https://notary-log-{my-handle}.zocomputer.io/api/health
14. service_doctor(service="notary-log")
F. REPORT TO ME
15. Public app URL
16. Exact Zo Backup Key from logs
17. Exact Zo Intake Token from logs (also in Documents/Notary Journal/.zo-intake-token)
18. Whether Google Drive was configured at build (yes/no)
19. Optional: set ZO_API_KEY in Zo Advanced for intake emails
G. IN THE APP
20. Open the public URL → Settings → paste Zo Backup Key → paste Zo Computer Form Token → Copy Intake Link → test /intake?key=<token>
21. If Google was configured at build: Settings → Cloud Backup should show “Connect Google Drive”, not “Contact the app administrator”
On first start with an empty database, the server creates the notary user and intake token automatically — no manual SQL. Optional runtime env on the Zo service: NOTARY_NAME, NOTARY_EMAIL (defaults: Primary Notary / notary@localhost).
Use this if you skipped Google at deploy or only added VITE_GOOGLE_CLIENT_ID to Zo Advanced after the app was already built. You must rebuild — Zo Settings env vars do not update an existing build.
1. Add VITE_GOOGLE_CLIENT_ID in Zo Advanced (if not already there).
2. In Google Cloud Console, add your app URL to Authorized JavaScript origins (e.g. https://notary-log-{your-handle}.zocomputer.io).
3. Paste this in Zo AI chat:
Enable Google Drive backup for Notary Journal on this Zo Computer.
REPO: /home/workspace/Notary-log
1. cd /home/workspace/Notary-log && git pull
2. Read VITE_GOOGLE_CLIENT_ID from Zo Advanced environment variables (or ask me to paste it).
3. Write artifacts/notary-journal/.env with exactly:
VITE_GOOGLE_CLIENT_ID=<that value>
(Zo Advanced env alone is not enough — this file must exist before build.)
4. bun run build — if this fails, stop and report the error
5. Restart the notary-log service (list_user_services → restart or re-register if needed)
6. Confirm in logs that the service is listening again
7. Tell me to hard-refresh the app in the browser, then check Settings → Cloud Backup:
- Should show “Connect Google Drive”
- Must NOT show “Contact the app administrator”
Same as the “later” flow, without Zo chat — run on the Zo machine:
cd /home/workspace/Notary-log
# Use the same client ID as in Zo Advanced:
echo 'VITE_GOOGLE_CLIENT_ID=your-google-client-id-here.apps.googleusercontent.com' > artifacts/notary-journal/.env
bun run build
# Then restart the notary-log service- One HTTP service per account on free tier
- Backups stored on the server (encrypted)
- Google Drive requires
VITE_GOOGLE_CLIENT_IDinartifacts/notary-journal/.envbeforebun run build(see Google Drive backup and Zo environment variables and Enable Google Drive backup later)
Extended troubleshooting: docs/zo-computer-setup.md.
Deploy to Cloudflare's edge network. Free tier, fast globally. No server-side backup — use JSON export/import or Google Drive.
# Install Cloudflare's CLI tool globally
npm install -g wrangler
# Log in to your Cloudflare account (opens browser)
npx wrangler loginFrom the repo root:
# Optional: set Google Drive backup client ID (skip if you don't need Google Drive)
export VITE_GOOGLE_CLIENT_ID="your-client-id-here.apps.googleusercontent.com"
# Build the app
pnpm --filter @workspace/notary-journal... run build# Deploy using the project's deploy script (builds + deploys in one step)
pnpm run deploy:cloudflareOr if you already built in Step 2:
# Deploy without rebuilding
node scripts/cloudflare-deploy.mjs --skip-buildOpen your *.workers.dev URL in a browser. Confirm you see the PIN setup screen.
SPA routing: The wrangler.toml file already includes not_found_handling = "single-page-application", so deep links like /journal work correctly. No extra _redirects file needed.
Note: Zo backup (/api/backup) is not available on Cloudflare Workers. Use JSON export/import or Google Drive backup from within the app.
No git account needed. Build locally, upload a zip, done.
If you don't have pnpm, install it:
npm install -g pnpmFrom the repo root:
# OPTIONAL: Set Google Drive backup client ID. Skip this line if you don't need Google Drive.
# This value gets baked into the JavaScript — you can't change it later without rebuilding.
export VITE_GOOGLE_CLIENT_ID="your-client-id-here.apps.googleusercontent.com"
# Build the app (compiles source code into production-ready files)
pnpm --filter @workspace/notary-journal run build
# Add the SPA redirect file (required so deep links like /journal don't 404)
echo '/* /index.html 200' > artifacts/notary-journal/dist/public/_redirects
# Create a zip of the build output (the zip contents should have index.html at the top level)
( cd artifacts/notary-journal/dist/public && zip -r ../../../../notary-journal-netlify.zip . )No
zipcommand? Install it:apt-get install zip(Linux),brew install zip(Mac), or on Windows usetar -a -c -f notary-journal-netlify.zip *from within thedist/publicfolder instead.
You now have notary-journal-netlify.zip at the repo root.
- Go to app.netlify.com/drop
- Drag the
notary-journal-netlify.zipfile onto the drop zone - Wait ~10 seconds. Status changes: Uploading > Processing > Published
- Netlify shows your live URL (something like
https://random-name-12345.netlify.app)
Open the Netlify URL in a browser. Confirm you see the PIN setup screen. Then navigate to your-url/journal — it should still load the app (not a 404). If it does, the _redirects file is working.
Important about VITE_GOOGLE_CLIENT_ID: This value is baked into the build at Step 2. Netlify's dashboard environment variables are ignored for drag-and-drop deploys. If you forget to set it before building, Google Drive backup won't work, and you'll need to rebuild and re-upload.
Every time you push to GitHub, Netlify auto-rebuilds and redeploys.
# From the repo root (if not already on GitHub)
git remote add origin https://github.com/YOUR-USERNAME/Notary-log.git
git branch -M main
git push -u origin main- Go to app.netlify.com > Add new site > Import an existing project
- Choose your Git provider (GitHub, GitLab, or Bitbucket)
- Select the
Notary-logrepo - Netlify reads
netlify.tomlautomatically — the build command and publish directory are already set. Accept the defaults. - Click Deploy site
First build takes 2-4 minutes.
- In Netlify, go to Site settings > Build & deploy > Environment
- Click Add a variable
- Key:
VITE_GOOGLE_CLIENT_ID| Value: your Google OAuth client ID - Save
Important: VITE_GOOGLE_CLIENT_ID is a build-time variable. After setting it, trigger a new deploy: go to Deploys > Trigger deploy > Deploy site. The value is baked into the JavaScript bundle — it's not read at runtime.
Open your Netlify URL (something like https://your-site-name.netlify.app). Confirm you see the PIN setup screen. Test a deep link like your-url/entry/test — it should load the app, not a 404.
Like Cloudflare Workers but git-connected. Uses _redirects for SPA routing.
Same as Option 4, Step 1.
- Go to Cloudflare Dashboard > Workers & Pages > Create > Pages > Connect to Git
- Select your
Notary-logrepo - Configure the build settings:
| Field | Value |
|---|---|
| Framework preset | None (or "Vite" if listed) |
| Build command | pnpm --filter @workspace/notary-journal... run build && echo '/* /index.html 200' > artifacts/notary-journal/dist/public/_redirects |
| Build output directory | artifacts/notary-journal/dist/public |
| Root directory | (leave blank — repo root is correct) |
Under Environment variables (build):
| Variable | Value |
|---|---|
NODE_VERSION |
22 |
VITE_GOOGLE_CLIENT_ID |
Your Google OAuth client ID (skip if not using Google Drive) |
Click Save and Deploy. First build takes 2-4 minutes.
Open your *.pages.dev URL. Confirm you see the PIN setup screen. Test a deep link like your-url/journal — it should load the app (not a 404). The _redirects file generated in the build command handles this.
Upload to any Apache/LiteSpeed-based web host (Hostinger, Bluehost, GoDaddy, etc.).
From the repo root:
# OPTIONAL: Set Google Drive client ID (skip if you don't need Google Drive)
export VITE_GOOGLE_CLIENT_ID="your-client-id-here.apps.googleusercontent.com"
# Build the app
pnpm --filter @workspace/notary-journal run buildCreate a file at artifacts/notary-journal/dist/public/.htaccess with this exact content:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>This tells Apache to serve index.html for any URL that isn't a real file — this is what makes deep links like /journal work instead of returning 404 errors.
- Log into your hosting control panel (e.g., Hostinger hPanel)
- Open File Manager
- Navigate to your public directory (usually
public_html) - Upload all files and folders from
artifacts/notary-journal/dist/public/intopublic_htmlindex.htmlshould be directly insidepublic_html.htaccessshould be directly insidepublic_html- The
assets/folder should be directly insidepublic_html
Tip: You can zip the contents of
dist/publicfirst, upload the zip, then extract it in the file manager. Make sure the zip containsindex.htmlat its top level, not inside a subfolder.
Open your domain in a browser. Confirm you see the PIN setup screen. Navigate to yourdomain.com/journal — it should still load the app. If you see a 404, confirm that .htaccess was uploaded and that your host has mod_rewrite enabled.
This section is only needed if you want the Cloud Backup feature in the app (direct backup to your Google Drive). Everything else works without it. Skip this section if you're using Zo backup or manual JSON export/import.
- Go to console.cloud.google.com
- Click Create Credentials > OAuth client ID
- Application type: Web application
- Under Authorized JavaScript origins, add your development URL:
http://localhost:5173 - Click Create
- Copy the Client ID (looks like
123456-abc123.apps.googleusercontent.com)
See the VITE_GOOGLE_CLIENT_ID instructions in each deployment option above. The key rule:
This value is baked into the JavaScript at build time. If you change it, you must rebuild and redeploy.
After your site is live:
- Go back to console.cloud.google.com
- Click your OAuth client ID
- Under Authorized JavaScript origins, add your full deployed URL (no trailing slash), e.g.:
https://your-site.netlify.apphttps://your-site.pages.devhttps://your-domain.com
- Click Save
Google takes a few seconds to ~5 minutes to propagate. If you get an "origin not allowed" error, wait a couple minutes and try again.
If you have multiple deployment URLs (e.g., a test deploy and a production deploy), add every one to Authorized JavaScript origins.
Let clients fill out their information before the appointment. When they submit, you get an email and the data appears in your app's Requests tab. Tap "Accept" to auto-fill a new journal entry.
Cost: $0. Uses Web3Forms — free, no signup required.
Client opens your intake link on their phone
-> Fills form (name, ID details, uploads ID photos, e-signs)
-> Submits -> You get an email via Web3Forms
-> The request appears in your app's Requests tab
You tap "Accept" -> Prefilled journal entry is created
| Platform | Email (Web3Forms) | Pending Queue | Cost |
|---|---|---|---|
| Zo Computer | Yes | Yes (built-in) | $0 |
| Cloudflare Workers/Pages | Yes | Yes (KV namespace) | $0 |
| Netlify | Yes | Yes | $0 |
| Hostinger / Shared Hosting | Yes | No (no server) | $0 |
- Go to web3forms.com → get your free access key
- Open your Notary Journal app → Settings > Client Intake Form
- Paste your Web3Forms Access Key → click Save
- Copy the generated intake link and share it with clients
That's it. No webhook configuration needed — the form handles both email and in-app queue automatically.
The intake link includes your key (e.g.,
?key=86e34...). This is a public form identifier, not a secret. Share it freely via text, email, or QR code.
Most common cause: The build output files weren't uploaded correctly.
- Netlify drag-and-drop: The zip must contain
index.htmlat its root level, not inside adist/publicfolder. Unzip the file on your computer — if you seedist/public/index.html, you zipped the wrong thing. Re-zip the contents ofdist/public, not the folder itself. - All hosts: Confirm the build completed without errors. Run
pnpm run buildand check for red error text.
The host doesn't know to serve index.html for non-file URLs.
- Netlify drag-and-drop: Make sure
_redirectswas added (see Option 3, Step 2). The file must be at the root of your zip alongsideindex.html. - Netlify git-connected / Cloudflare Pages: The
_redirectsis generated in the build command. Check the deploy log to confirm it ran. - Cloudflare Workers: Check
wrangler.tomlhasnot_found_handling = "single-page-application". - Hostinger / Apache: Confirm
.htaccessis uploaded topublic_htmlalongsideindex.html. Check thatmod_rewriteis enabled on your host.
The VITE_GOOGLE_CLIENT_ID wasn't set when the app was built.
- Zo Computer: Zo Advanced environment variables do not enable Google Drive by themselves. Use the Enable Google Drive backup later (Zo) prompt (write
.env,bun run build, restart service). - Git-connected deploys (Netlify, Cloudflare Pages): Set the variable in the host dashboard, then trigger a rebuild.
- Drag-and-drop / manual deploys: Set
VITE_GOOGLE_CLIENT_IDin your shell before runningpnpm run build, then rebuild and re-upload. Host dashboard environment variables are ignored for drag-and-drop.
Chrome cached a previous working version via the service worker. Fix:
- In Chrome: Settings > Privacy and security > Site settings > All sites > find your domain > Clear & reset
- Reload the page
The journal stores data in IndexedDB, which is per-domain in the browser. Two different URLs = two separate journals.
To move data between domains:
- Google Drive backup: Settings > Cloud Backup > Backup on source, Restore on destination
- JSON export/import: Settings > Export all > JSON on source, then Import on destination
CSV and PDF exports are read-only — they cannot be re-imported.
The build didn't produce the asset files, or the host isn't serving the assets/ folder.
- Run
pnpm run buildagain - Confirm
artifacts/notary-journal/dist/public/assets/exists and contains.jsand.cssfiles - Re-upload/re-deploy
- Confirm the backup API URL is correct:
https://notary-log-{your-handle}.zocomputer.io/api/backup - Confirm the backup key matches what
zo logs --service notary-logshows - Check the Zo service logs for server errors:
zo logs --service notary-log - Make sure you didn't create a second Zo service — the backup runs in the same service as the app
| File | Purpose |
|---|---|
netlify.toml |
Netlify git-connected build config + SPA redirect |
wrangler.toml |
Cloudflare Workers static assets + SPA fallback + KV for intake |
zosite.json |
Zo publish configuration |
server.ts |
Zo server — serves static files + Zo backup API |
cloudflare/worker.ts |
Cloudflare Workers entry point + intake webhook/KV |
artifacts/notary-journal/src/ |
Main app source (pages, components, lib) |
artifacts/notary-journal/src/pages/client-requests.tsx |
Pending intake requests UI |
artifacts/notary-journal/src/pages/client-intake.tsx |
Client-facing intake form |
artifacts/notary-journal/src/lib/gdrive.ts |
Google Drive backup logic |
artifacts/notary-journal/src/lib/intake-api.ts |
Intake API client (talks to server) |
This project is licensed under a Custom Non-Commercial License. Free for personal use — commercial use requires a written agreement. See LICENSE for full terms.
Questions about commercial licensing? Contact Joseph Iannazzi at joseph@justlegalsolutions.org or iannazzi.joseph@gmail.com.