Protocol form:
- Title: 'Round Robin Check-In' → 'Name of the protocol'
- Source: 'Group Works Deck (or @username)' → 'Where it comes from (or @username)'
Collection form:
- Title: 'Protocols for Mutual Aid Groups' → 'Name of the collection'
- Source: 'Author or community (or @username)' → 'Where it comes from (or @username)'
Registration form:
- Display Name: 'Jane Doe' → 'Your name'
Browser tab titles:
- Protocol pages: 'Title · Site Name'
- Collection pages: 'Title · Site Name'
- User pages: '@username · Site Name'
- Resets to site name on library/collections/about
Social media previews (server-side, for crawlers):
- index.php detects /protocols/slug, /collections/slug, /users/username
- Looks up title + description from DB
- Injects og:title, og:description, og:type, og:site_name, og:url
- Injects twitter:card, twitter:title, twitter:description
- Replaces default <title> with enriched version
Frontend:
- Default og/twitter meta tags on homepage
- Path-based URLs (shared links) redirect to hash routes for SPA routing
Collection source:
- Collection list: don't default to @author as source label
- Collection detail: suppress SOURCE when it equals @author
- Collection detail: 'curated by' instead of 'authored by'
- No source shown when field is empty
Back button:
- isRouting guard on route() prevents showDetail, showCollectionDetail,
showUser, and navigate from setting location.hash when called from
a hashchange event (back button). This prevents duplicate history
entries that trap the user.
Home click:
- navigate('library') no longer blocked by currentRoute guard
(view !== 'library' exception), so clicking the site name always
returns to the homepage.
The showUser() function was setting detailActions to empty string
instead of rendering the admin buttons (Change Password, Load Seed
Protocols, Import YAML Files). Now properly renders buttons every
time the profile loads, so they persist after import operations.
Also restores the missing JS functions: importYamlToForm,
bulkImportYaml, showChangePassword, handleChangePassword,
loadSeedProtocols, closePwModal.
Three recurring issues fixed together:
1. Ctrl+click: All protocol and collection pills now use <a href>
instead of <div onclick>, so ctrl/cmd+click opens in new tab.
text-decoration:none and color:inherit prevent link styling.
2. Consistent modules: Shared renderProtocolPill() and
renderCollectionPill() functions used everywhere — library,
collections list, collection detail, user profile. All produce
identical HTML with .protocol-grid wrapper for consistent spacing.
Profile collections no longer have .icon divs.
3. Header layout: Logo (32px) on left with crumb-text wrapper.
$ protocol-droid on first line, crumb path below it.
Favicon links and PWA manifest restored.
Relative paths (frontend/ not /frontend/) for subdirectory support.
Authoring page:
- 'Import YAML' button next to Save/Cancel on the create form
- Opens file picker for .yaml/.yml files
- Imports the YAML via API, then loads it into the form for editing
- Bad YAML is rejected by the API with an error message shown via toast
Admin profile (own profile page):
- 'Load Seed Protocols' button (loads 100 built-in seeds)
- 'Import YAML Files' button (multi-file picker)
- Bulk imports each file, reports count of imported vs failed
- Failed files are listed with error reasons in the toast
- Profile refreshes after import to show new protocols
Password change:
- 'Change Password' button on own profile for all users
- Modal with current/new/confirm password fields
- New API endpoint POST /api/auth/password
All imports validate format — incorrectly formatted files are
discarded and the user is informed which files failed and why.
Link tags for logo.svg, favicon.ico, icon-192.png, and manifest.json
were missing from the HTML head. Also fixed stylesheet path from
absolute (/frontend/) to relative (frontend/) for subdirectory support.
- Logo 32px to fill more of the header height
- Logo on left, text + crumb path stacked to its right (not below)
- .crumb-text wrapper keeps $ protocol-droid and path together
- Path aligns with the $ sign, not underneath the logo
- Logo (24px) sits to the LEFT of $ protocol-droid on the first line
- Crumb path wraps to second line below
- Both in a column layout via .crumb-main wrapper
- Logo is clearly visible at 24x24px
- Smaller head, removed mouth grill, bigger cuter eyes with green centers
- Compact viewBox (100x100) with no empty space below shoulders
- Logo sized 18x22px in header — fits within the bar without resizing it
- Regenerated all PWA icons (192, 512) and favicon
- Geometric Companion logo: rounded head, blue outline, green eyes,
green antenna tip. SVG for crisp rendering at any size.
- Logo appears in header next to site name
- PWA manifest (manifest.json) with 192px and 512px PNG icons
- Favicon (favicon.ico) for browser tab
- Apple touch icon for iOS home screen
- README: logo replacement instructions for whitelabeling
To replace the logo for whitelabeling, just swap frontend/logo.svg
and the icon PNGs. Update <link> tags in index.html if filename changes.
The isRouting flag prevents showDetail, showCollectionDetail,
showUser, and navigate from setting location.hash when called
from route() (triggered by back button hashchange). This was
creating duplicate history entries that trapped the user.
Also fixed: init() re-route check now uses #protocols not #library,
and removed duplicate location.hash = 'about' in navigate().
- Removed white-space:nowrap from .meta so source text wraps
- Kept max-width:140px so description still gets the majority of space
- Steps count stays on one line (nowrap only on .steps)
- .info now uses flex: 1 1 auto (grows to fill available space)
- .meta constrained to max-width: 120px with ellipsis truncation
- Long source text no longer squeezes the description — description
takes the majority of the width, source is truncated if too long
- 'Load Seed Protocols' button now appears on the admin's own profile
page instead of the About page. Can be repeated to re-import seeds.
- After loading, the profile refreshes to show updated protocol list.
- 'Change Password' button also appears on own profile page for all
users (not just admin), opening a modal with current/new/confirm fields.
- Password modal added to HTML.
- README updated: seed loading instructions point to profile page,
mentions 100 protocols, notes repeat seeding is supported.
When the back button triggers hashchange -> route(), the isRouting
flag prevents navigate() and showDetail() from re-setting location.hash,
which was creating duplicate history entries and trapping the user.
Now the back button properly traverses history.
- Add text-decoration:none and color:inherit to .protocol-pill so
<a> tags don't show underlines or link colors
- Guard hash setting in showDetail/showCollectionDetail/showUser:
only set location.hash if it's not already the target, preventing
the redundant hashchange that was breaking the back button
- Protocol and collection pills now use <a href> instead of <div onclick>,
so ctrl+click (or cmd+click) opens in a new tab, and middle-click works
- Applied consistently: library, collection detail, user profile
- After deleting a protocol or collection, currentRoute is reset so
navigation back to the list actually works
- Password change: when viewing your own profile, a 'Change Password'
button appears. Opens a modal requiring current password + new password
+ confirmation. New API endpoint POST /api/auth/password validates
the current password and updates the hash.
- Sort toggle now says 'most recent' instead of 'date added'
- Sort logic uses index position instead of text matching
- Detail pages show just the date without 'added' prefix
Rendering consistency:
- Create shared renderProtocolPill() and renderCollectionPill() functions
used everywhere: library, collection detail, user profile, picker
- All protocol/collection modules now look identical across all contexts
- Profile page collections and protocols use the same shared renderers
- Protocol grids wrapped in .protocol-grid for consistent spacing
New features:
- Footer with CC-BY 4.0 license link and Terms of Service link
- Terms of Service page stating admin reserves right to remove content
- CC-BY 4.0 default license for submitted content
Deployment:
- README now recommends git clone for initial deployment
- README documents git pull for updates without data loss
- All nav links use #protocols instead of #library
Bug fixes (from previous commits, re-applied to current codebase):
- Delete redirect: reset currentRoute guard so navigate works after delete
- #protocols routing instead of #library
- forked_from: null no longer shows as clickable link
- Source field suppressed when it duplicates author
- Collections say 'curated by' instead of 'authored by'
- Breadcrumb home click works from detail pages
- Search debounce and double-navigation guard
- When source equals @username (same as the author), don't show SOURCE line
- When source is empty, don't show a leading · separator before 'authored by'
- Collections say 'curated by' instead of 'authored by'
- Fix YAML fallback parser to properly handle folded scalars (>-) at
both top-level (description, outcome, practice) and step-level
(step descriptions). Previously stored '>-' as the literal value
instead of the folded text.
- Fix delete protocol/collection: reset currentRoute guard so
navigate('library') actually works after deletion.
- Change #library hash to #protocols throughout (routing, nav links,
breadcrumb shows 'protocols/' on the main page).
- Fix breadcrumb home click not working from detail pages (allow
re-navigation to library even if currentRoute matches).
- Fix forked_from: null showing as a clickable link — now suppressed
when value is null or the string 'null'.
- README: replace curl seed instructions with About page button,
add About page customization instructions for whitelabeling.
index.html uses absolute /frontend/ paths for CSS and JS, and app.js
hardcoded const API = '/api'. In subdirectory deployments these resolve
to the wrong URL, causing unstyled HTML and broken API calls.
- index.php: rewrite /frontend/ references in index.html with detected basePath
- app.js: derive BASE_PATH from the script's own URL and prefix API calls
A tool for authoring, sharing, and curating social protocols.
Features:
- Protocol library with search, tag filtering, and sort by date/relevance
- Protocol authoring with structured fields (title, description, steps, outcome, practice, source, tags)
- Protocol forking with provenance tracking
- Collection creation with searchable protocol picker and ordering
- User accounts with roles (admin, member, viewer)
- YAML import/export for portability
- Self-hostable on LAMP/Cloudron, works in subdirectories
- Responsive design with hamburger menu on mobile
- About page
Security:
- CSRF protection via Origin/Referer validation
- Session regeneration on login/register
- Secure session cookie params (HttpOnly, SameSite, Secure)
- Visibility enforcement on private/unlisted items
- YAML object injection hardening
- Login rate limiting
- Path traversal protection
- Input validation and length clamping
- Vote value constraining
Stack: PHP 8.x + MySQL/MariaDB, vanilla JS frontend, no external dependencies.
Hippocratic License (HL3-CORE).