You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: Account-free outlet submissions using a GitHub App
Summary
Allow visitors to submit public charging-outlet locations through the form on plug.vln.gg without requiring a GitHub account.
Use a stateless email-verification service and a narrowly permissioned GitHub App to convert verified submissions into GitHub Issues. GitHub Actions will validate accepted submissions, insert them into our existing SQLite database, regenerate the public map data, open a pull request, and automatically merge it after all required checks pass.
Problem
PLUG is hosted on GitHub Pages, which only serves static HTML, CSS, and JavaScript. It cannot securely:
Store GitHub credentials.
Sign GitHub App requests.
Send verification emails.
Write directly to the repository.
Insert records into SQLite.
Requiring contributors to create a GitHub account would also create unnecessary friction for the people PLUG is intended to serve.
A visitor completes the outlet-submission form on plug.vln.gg.
The browser performs basic field validation.
The normalized submission is sent to a stateless edge function.
The function applies rate limits.
The function encrypts and signs a short-lived verification token.
A verification link is emailed to the submitter.
The link opens a confirmation page on plug.vln.gg.
The visitor explicitly confirms the submission with a POST request.
The function verifies the signature, expiration and submission identifier.
The PLUG GitHub App creates a labeled submission issue.
GitHub Actions validates and sanitizes the issue.
A parameterized SQLite statement inserts the new location into the existing venues table in data/locations.db.
The Action regenerates public/data/locations.json.
The Action opens a pull request containing the proposed change.
Required integrity, schema, duplicate and security checks run.
The pull request is automatically merged after all checks pass.
GitHub Pages deploys the updated map via the existing deploy.yml workflow.
The original submission issue is closed with the result.
GitHub App Permissions
Install the GitHub App only on Fused-Gaming/plug.
Permission
Access
Purpose
Metadata
Read
Basic repository identification
Issues
Read and write
Create and update submission issues
Contents
None
Prevent direct repository modification
Pull requests
None
GitHub Actions creates pull requests
Actions
None
Prevent workflow modification or execution
Administration
None
Prevent repository configuration changes
The GitHub App must not receive permission to modify source code, workflows, SQLite files, branches or pull requests.
Verification Token
The encrypted token may contain:
{
"submissionId": "sub_01K...",
"emailHash": "sha256...",
"outlet": {
"name": "Oakland Main Library",
"description": "Outlets available near the public computer area.",
"address": "125 14th Street, Oakland, CA",
"latitude": 37.8002,
"longitude": -122.2631,
"locationType": "library",
"accessHours": "Monday–Saturday, 10:00 AM–5:30 PM",
"accessible": true,
"indoor": true,
"requiresPurchase": false
},
"issuedAt": 1785110000,
"expiresAt": 1785111800
}
Requirements:
Encrypt and authenticate the token.
Expire tokens after 15–30 minutes.
Do not expose the submitter's email address in the URL.
Do not store the email address in GitHub.
Require a confirmation POST after opening the link.
Generate a deterministic or unique submission identifier.
Reject expired, modified or malformed tokens.
SQLite Schema
Correction: our canonical database is data/locations.db, and it already has a venues table (schema defined in scripts/etl/db.js, not a standalone .sql file) that this feature's rows belong in — the same table scripts/etl/sync-locations.mjs and scripts/etl/ingest-submissions.mjs already read and write. This feature needs three new tracking columns on that existing table, not a separate outlets table:
-- New submission-tracking columns on the existing `venues` table in data/locations.dbALTERTABLE venues ADD COLUMN source_issue INTEGER UNIQUE;
ALTERTABLE venues ADD COLUMN submission_id TEXT UNIQUE;
ALTERTABLE venues ADD COLUMN submitted_by_hash TEXT;
The rest of this feature's fields already map onto existing venues columns:
This feature's field
Existing venues column
name
name
description
notes
address
address
latitude, longitude
lat, lon
locationType
category
accessHours
hours
indoor
indoor
accessible
appended to amenities (e.g. "Accessible", matching how public/scripts/landing.js already reads this flag)
requiresPurchase
reflected in access ("Open to everyone" vs "Customers only", matching .github/ISSUE_TEMPLATE/location.yml)
verification/review state
tier (e.g. "community") and verification_source, both already present
submission timestamp
first_seen, already present
All inserts must use parameterized statements. Never interpolate issue content into SQL or shell commands.
data/locations.db and public/data/locations.json are not listed above as new files — they already exist and are already maintained by scripts/etl/sync-locations.mjs and scripts/etl/ingest-submissions.mjs; this feature extends them rather than introducing parallel plug.sqlite / outlets.json files. GitHub Pages deployment also doesn't need a new deploy-pages.yml — merges to main already trigger the existing .github/workflows/deploy.yml.
Validation Requirements
Before creating or merging a pull request, verify:
Required fields are present.
Field lengths are within documented limits.
Latitude is between -90 and 90.
Longitude is between -180 and 180.
Coordinates are within the supported service region.
Location type is an allowed enum value.
URLs use an allowed protocol.
User content contains no executable HTML.
The submission ID has not been processed.
The source issue has not been processed.
The location is not an obvious duplicate.
The venues table schema in data/locations.db has not changed unexpectedly.
PRAGMA integrity_check returns ok.
Existing records were not unintentionally removed.
Store the App ID, installation ID and private key securely.
Confirm that the App cannot modify repository contents.
GitHub Actions and Data
Create the issue-processing workflow.
Implement structured issue parsing.
Implement submission validation.
Add the new source_issue, submission_id, and submitted_by_hash columns to the existing venues table in data/locations.db.
Insert records with parameterized SQL.
Regenerate public/data/locations.json.
Generate a human-readable change summary.
Create the submission pull request.
Configure required checks.
Configure qualified automatic merging.
Close or update processed issues.
Confirm the existing deploy.yml workflow picks up the merge (no new deploy workflow needed).
Non-Goals
Building a traditional user-account system.
Storing passwords.
Giving public users GitHub repository permissions.
Allowing the GitHub App to modify repository contents.
Real-time database writes from the browser.
Publishing contributor email addresses.
Automatically accepting submissions that fail geographic or duplicate checks.
Definition of Done
A visitor without a GitHub account can submit a public charging location, verify control of an email address, and receive confirmation. The verified submission is converted into a GitHub Issue by the narrowly scoped PLUG GitHub App, processed into data/locations.db by GitHub Actions, reviewed through an automatically generated pull request, merged after all required checks pass, and published on plug.vln.gg without maintaining a traditional application database.
feat: Account-free outlet submissions using a GitHub App
Summary
Allow visitors to submit public charging-outlet locations through the form on
plug.vln.ggwithout requiring a GitHub account.Use a stateless email-verification service and a narrowly permissioned GitHub App to convert verified submissions into GitHub Issues. GitHub Actions will validate accepted submissions, insert them into our existing SQLite database, regenerate the public map data, open a pull request, and automatically merge it after all required checks pass.
Problem
PLUG is hosted on GitHub Pages, which only serves static HTML, CSS, and JavaScript. It cannot securely:
Requiring contributors to create a GitHub account would also create unnecessary friction for the people PLUG is intended to serve.
Proposed Architecture
flowchart TD FORM["User submits outlet form"] RELAY["Stateless verification function"] TOKEN["Create encrypted submission token"] EMAIL["Send verification email"] CONFIRM["User confirms submission"] VERIFY{"Token valid?"} APP["PLUG GitHub App"] ISSUE["Create submission issue"] ACTION["Process submission Action"] VALIDATE{"Submission valid?"} DATABASE["Insert record into data/locations.db"] EXPORT["Regenerate public/data/locations.json"] PR["Create pull request"] CHECKS{"Required checks pass?"} MERGE["Automatically merge"] DEPLOY["Deploy GitHub Pages"] REJECT["Reject or request corrections"] FORM --> RELAY RELAY --> TOKEN TOKEN --> EMAIL EMAIL --> CONFIRM CONFIRM --> VERIFY VERIFY -->|No| REJECT VERIFY -->|Yes| APP APP --> ISSUE ISSUE --> ACTION ACTION --> VALIDATE VALIDATE -->|No| REJECT VALIDATE -->|Yes| DATABASE DATABASE --> EXPORT EXPORT --> PR PR --> CHECKS CHECKS -->|No| REJECT CHECKS -->|Yes| MERGE MERGE --> DEPLOYSubmission Flow
plug.vln.gg.plug.vln.gg.venuestable indata/locations.db.public/data/locations.json.deploy.ymlworkflow.GitHub App Permissions
Install the GitHub App only on
Fused-Gaming/plug.The GitHub App must not receive permission to modify source code, workflows, SQLite files, branches or pull requests.
Verification Token
The encrypted token may contain:
{ "submissionId": "sub_01K...", "emailHash": "sha256...", "outlet": { "name": "Oakland Main Library", "description": "Outlets available near the public computer area.", "address": "125 14th Street, Oakland, CA", "latitude": 37.8002, "longitude": -122.2631, "locationType": "library", "accessHours": "Monday–Saturday, 10:00 AM–5:30 PM", "accessible": true, "indoor": true, "requiresPurchase": false }, "issuedAt": 1785110000, "expiresAt": 1785111800 }Requirements:
SQLite Schema
Correction: our canonical database is
data/locations.db, and it already has avenuestable (schema defined inscripts/etl/db.js, not a standalone.sqlfile) that this feature's rows belong in — the same tablescripts/etl/sync-locations.mjsandscripts/etl/ingest-submissions.mjsalready read and write. This feature needs three new tracking columns on that existing table, not a separateoutletstable:The rest of this feature's fields already map onto existing
venuescolumns:venuescolumnnamenamedescriptionnotesaddressaddresslatitude,longitudelat,lonlocationTypecategoryaccessHourshoursindoorindooraccessibleamenities(e.g."Accessible", matching howpublic/scripts/landing.jsalready reads this flag)requiresPurchaseaccess("Open to everyone"vs"Customers only", matching.github/ISSUE_TEMPLATE/location.yml)tier(e.g."community") andverification_source, both already presentfirst_seen, already presentAll inserts must use parameterized statements. Never interpolate issue content into SQL or shell commands.
Repository Changes
data/locations.dbandpublic/data/locations.jsonare not listed above as new files — they already exist and are already maintained byscripts/etl/sync-locations.mjsandscripts/etl/ingest-submissions.mjs; this feature extends them rather than introducing parallelplug.sqlite/outlets.jsonfiles. GitHub Pages deployment also doesn't need a newdeploy-pages.yml— merges tomainalready trigger the existing.github/workflows/deploy.yml.Validation Requirements
Before creating or merging a pull request, verify:
-90and90.-180and180.venuestable schema indata/locations.dbhas not changed unexpectedly.PRAGMA integrity_checkreturnsok.public/data/locations.jsonmatchesdata/locations.db.Abuse Prevention
Automatic Merge Policy
A generated pull request may be automatically merged only when:
public/data/locations.jsonmatchesdata/locations.db.Submissions that fail or produce uncertain results must remain open for human review.
Acceptance Criteria
venuestable ofdata/locations.db.public/data/locations.jsonis regenerated automatically.Implementation Tasks
Frontend
Verification Function
GitHub App
Fused-Gaming/plug.GitHub Actions and Data
source_issue,submission_id, andsubmitted_by_hashcolumns to the existingvenuestable indata/locations.db.public/data/locations.json.deploy.ymlworkflow picks up the merge (no new deploy workflow needed).Non-Goals
Definition of Done
A visitor without a GitHub account can submit a public charging location, verify control of an email address, and receive confirmation. The verified submission is converted into a GitHub Issue by the narrowly scoped PLUG GitHub App, processed into
data/locations.dbby GitHub Actions, reviewed through an automatically generated pull request, merged after all required checks pass, and published onplug.vln.ggwithout maintaining a traditional application database.