toggle-sidebar action was only in the DashboardView inline script, not in dashboard.js where toggleSidebar() is defined. Move it into the dashboard.js event delegation switch so it's guaranteed to fire. Also fix beta webhook: was using a different secret than production so Gitea pushes to development never triggered the beta deploy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tinker Tickets
A feature-rich PHP-based ticketing system designed for tracking and managing data center infrastructure issues with enterprise-grade workflow management and a retro terminal aesthetic.
Documentation: Wiki Design System: web_template — shared CSS, JS, and layout patterns for all LotusGuild apps
Styling & Layout
Tinker Tickets uses the LotusGuild Terminal Design System. For all styling, component, and layout documentation see:
web_template/README.md— full component reference, CSS variables, JS APIweb_template/base.css— unified CSS (.lt-*classes)web_template/base.js—window.ltutilities (toast, modal, CSRF, fetch helpers)web_template/php/layout.php— PHP base layout template
Key conventions:
- All
.lt-*CSS classes come frombase.css— do not duplicate them inassets/css/ - All
lt.*JS utilities come frombase.js— uselt.toast,lt.modal,lt.api, etc. - CSP nonces: every
<script>tag needsnonce="<?php echo $nonce; ?>" - CSRF: inject
window.CSRF_TOKENvia the nonce-protected inline script block;lt.api.*adds the header automatically
Design Decisions
The following features are intentionally not planned for this system:
- Email Integration: Matrix (hookshot webhook) is the chosen external notification method
- Time Tracking: Out of scope for current requirements
- OAuth2/External Identity Providers: Authelia is the only approved SSO method
Core Features
Dashboard & Ticket Management
- View Modes: Toggle between Table view and Kanban card view (drag-and-drop status changes)
- Right Drawer Preview: Click any ticket title to open a quick-preview panel without navigating away
- Stats Widgets: Clickable cards for quick filtering (Open, Critical, Unassigned, Today's tickets) with live trend indicators
- Charts: Priority distribution donut, status breakdown donut, and category bar chart (Chart.js, CDN)
- Team Workload: Collapsible panel showing open ticket count per assignee with progress bars
- Full-Text Search: Search across tickets, descriptions, and metadata
- Advanced Search: Date ranges (Flatpickr), priority ranges, user filters
- Saved Filters: Save and recall filter presets; quick-switch pills above the table
- Column Visibility: Toggle which dashboard table columns are shown; persisted in localStorage
- Ticket Assignment: Assign tickets to specific users with typeahead search
- Priority Tracking: P1 (Critical) to P5 (Minimal Impact) with color-coded indicators and status dots
- Custom Categories: Hardware, Software, Network, Security, General
- Ticket Types: Maintenance, Install, Task, Upgrade, Issue, Problem
- Skeleton Loaders: Loading placeholders during filter changes and data refresh
- Export: Export filtered tickets to CSV or JSON format
- Ticket Linking: Reference other tickets in comments using
#123456789format
Ticket Visibility Levels
- Public: All authenticated users can view the ticket
- Internal: Only users in specified groups can view the ticket (at least one group required)
- Confidential: Only the creator, assignee, and admins can view the ticket
Workflow Management
- Status Transitions: Enforced workflow rules (Open → Pending → In Progress → Closed)
- Comment Requirements: Transitions that require a comment open an inline modal before committing the change
- Workflow Designer: Visual admin UI at
/admin/workflowto configure transitions - Workflow Validation: Server-side validation prevents invalid status changes
- Admin Controls: Certain transitions can require admin privileges
Collaboration Features
- Markdown Comments: Full Markdown support with live preview, toolbar, and table rendering
- @Mentions: Tag users in comments with
@autocomplete (typeahead); triggers Matrix notification to mentioned user - Comment Edit/Delete: Comment owners and admins can edit or delete comments
- Auto-linking: URLs in comments are automatically converted to clickable links
- File Attachments: Upload files to tickets with drag-and-drop; image attachments display as thumbnails with lightbox zoom
- Ticket Cloning: Duplicate any ticket with a single click; auto-links as
relates_to - Ticket Dependencies: Link tickets as blocks / blocked-by / relates-to / duplicates
- Duplicate Detection: Similarity check on ticket title surfaces potential duplicates with one-click linking
- Activity Timeline: Full
lt-timelineaudit trail — color-coded by event type (status, comment, assign, attach) - Watcher Avatars: Avatar group shows who is watching a ticket; tooltip lists all names
- SLA Timer: P1/P2 tickets display a live elapsed-time banner with progress bar (P1 = 8 h, P2 = 24 h, P3 = 72 h)
- Priority Alert Banner: P1 shows a sticky error banner; P2 shows a warning banner — dismissible per session
Ticket Templates
- Template Management: Admin UI at
/admin/templatesto create/edit templates - Quick Creation: Pre-configured templates for common issues
- Auto-fill: Templates populate title, description, category, type, and priority
Recurring Tickets
- Scheduled Tickets: Automatically create tickets on a schedule
- Admin UI: Manage at
/admin/recurring-tickets - Flexible Scheduling: Daily, weekly, or monthly recurrence
- Cron Integration: Run
cron/create_recurring_tickets.phpto process
Custom Fields
- Per-Category Fields: Define custom fields for specific ticket categories
- Admin UI: Manage at
/admin/custom-fields - Field Types: Text, textarea, select, checkbox, date, number
- Required Fields: Mark fields as required for validation
API Key Management
- Admin UI: Generate and manage API keys at
/admin/api-keys - Bearer Token Auth: Use API keys with
Authorization: Bearer YOUR_KEYheader - Expiration: Optional expiration dates for keys
- Revocation: Revoke compromised keys instantly
User Management & Authentication
- SSO Integration: Authelia authentication with LLDAP backend
- Role-Based Access: Admin and standard user roles
- User Groups: Groups displayed in settings modal, used for visibility
- User Avatars: JPEG avatars fetched from lldap via LDAP; cached locally (
/api/user_avatar.php) - User Activity: View per-user stats at
/admin/user-activity - Session Management: Secure PHP session handling with timeout
Bulk Actions (Admin Only)
- Bulk Close: Close multiple tickets at once
- Bulk Assign: Assign multiple tickets to a user (typeahead search)
- Bulk Priority: Change priority for multiple tickets
- Bulk Status: Change status for multiple tickets
- Checkbox Click Area: Click anywhere in the checkbox cell to toggle
In-App Notifications
- Notification Bell: Header bell icon with unread count badge; polls every 60 s
- Notification Sources: Ticket assigned to you, comment on your ticket, status change on watched ticket, @mention
- Mark All Read: Click the bell or "Mark all read" to clear the badge
- Powered by audit_log: No extra table — notifications are derived from existing audit trail
Matrix Notifications (hookshot)
- Ticket Created: Fires when any ticket is created (manual or via API)
- Status Changed: Fires on every status transition
- @Mentions: Mentioned users receive a direct Matrix notification
- Assignment: Optional — set
MATRIX_NOTIFY_ASSIGNMENTS=1to enable - Comments: Optional — set
MATRIX_NOTIFY_COMMENTS=1to enable - Watcher Alerts: Watchers receive Matrix notifications on status changes (resolved via Synapse Admin API)
- Rich Payloads: JSON payloads sent to hookshot generic webhook; format ticket links using
APP_DOMAIN
Command Palette (Ctrl+K)
- Global Access: Available on every page via
Ctrl+Kor⌘Kbutton in header - Quick Navigation: Dashboard, New Ticket, My Tickets, admin pages
- Recent Tickets: Last 5 viewed tickets (stored in localStorage)
- Filter Shortcuts: Apply common filters directly from palette
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Ctrl/Cmd + K |
Open command palette (global) |
Ctrl/Cmd + E |
Toggle edit mode (ticket page) |
Ctrl/Cmd + S |
Save changes (ticket page) |
N |
New ticket (dashboard) |
J / K |
Next / previous row (dashboard table) |
Enter |
Open selected ticket (dashboard) |
G then D |
Go to dashboard |
1–4 |
Quick status change (ticket page) |
ESC |
Cancel edit / close modal |
? |
Show keyboard shortcuts help |
Security Features
- CSRF Protection: Token-based protection with constant-time comparison; token rotated after each write
- Rate Limiting: Session-based AND IP-based rate limiting to prevent abuse
- Security Headers: CSP with nonces (no unsafe-inline), X-Frame-Options, X-Content-Type-Options
- SQL Injection Prevention: All queries use prepared statements with parameter binding
- XSS Protection: HTML escaped in markdown parser, CSP headers block inline scripts
- Audit Logging: Complete audit trail of all actions
- Visibility Enforcement: Access checks on ticket views, downloads, and bulk operations
- Collision-Safe IDs: Ticket IDs verified unique before creation
Automated Ticket Creation (hwmonDaemon)
hwmonDaemon runs on all servers and creates tickets automatically for hardware/health issues. It calls the standalone API endpoint at the document root:
POST /create_ticket_api.php
Authorization: Bearer <api_key>
Content-Type: application/json
{
"title": "[hostname][auto][production][hardware][single-node] SMART issues on /dev/sda",
"description": "...",
"priority": "2",
"category": "Hardware",
"type": "Issue"
}
Key behaviours:
- Authenticated via
Authorization: Bearerheader — API key stored in/etc/hwmonDaemon/.env - Deduplication: Generates a SHA-256 hash from the issue category, hostname, and device; rejects duplicate tickets within 24 hours
- Cluster-wide issues (Ceph health, etc.) deduplicate across all nodes (hostname excluded from hash)
- Matrix notification sent automatically after ticket creation
- API key must be generated at
/admin/api-keys; the key goes in hwmonDaemon's/etc/hwmonDaemon/.envasTICKET_API_KEY
Technical Architecture
Backend
- Language: PHP 7.4+
- Database: MariaDB/MySQL
- Architecture: MVC pattern with models, views, controllers
- Authentication: Authelia SSO with LLDAP backend
Frontend
- HTML5/CSS3: Semantic markup with retro terminal styling
- JavaScript: Vanilla JS with Fetch API for AJAX
- Markdown: Custom markdown parser with toolbar
- Terminal UI: Box-drawing characters, monospace fonts, CRT effects
- Mobile Responsive: Touch-friendly controls, responsive layouts
- Chart.js: CDN-loaded on dashboard only — priority/status/category charts
- Flatpickr: CDN-loaded on dashboard only — date range filter pickers
Database Tables
| Table | Purpose |
|---|---|
tickets |
Core ticket data with visibility |
ticket_comments |
Markdown-supported comments |
ticket_attachments |
File attachment metadata |
ticket_dependencies |
Ticket relationships |
ticket_watchers |
Per-user ticket subscriptions |
users |
User accounts with groups |
user_preferences |
User settings (rows per page, notification opts, notif_last_seen) |
audit_log |
Complete audit trail (also powers in-app notifications) |
status_transitions |
Workflow configuration |
ticket_templates |
Reusable templates |
recurring_tickets |
Scheduled tickets |
custom_field_definitions |
Custom field schemas |
custom_field_values |
Custom field data |
saved_filters |
Saved filter combinations |
bulk_operations |
Bulk operation tracking |
api_keys |
API key storage with hashed keys |
tickets Table Key Columns
| Column | Type | Description |
|---|---|---|
ticket_id |
varchar(9) | Unique 9-digit identifier |
visibility |
enum | public, internal, confidential |
visibility_groups |
varchar(500) | Comma-separated group names (for internal) |
created_by |
int | Foreign key to users |
assigned_to |
int | Foreign key to users (nullable) |
updated_by |
int | Foreign key to users |
priority |
int | 1–5 (1=Critical, 5=Minimal) |
status |
varchar(20) | Open, Pending, In Progress, Closed |
Indexed Columns (performance)
tickets:ticket_id(unique),status,priority,created_at,created_by,assigned_to,visibilityaudit_log:user_id,action_type,entity_type,created_at
API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/create_ticket_api.php |
POST | Create ticket via API key (hwmonDaemon, external tools) |
/api/update_ticket.php |
POST | Update ticket with workflow validation |
/api/assign_ticket.php |
POST | Assign ticket to user |
/api/add_comment.php |
POST | Add comment to ticket |
/api/clone_ticket.php |
POST | Clone an existing ticket |
/api/get_template.php |
GET | Fetch ticket template |
/api/get_users.php |
GET | Get user list for assignments |
/api/bulk_operation.php |
POST | Perform bulk operations |
/api/ticket_dependencies.php |
GET/POST/DELETE | Manage dependencies |
/api/upload_attachment.php |
GET/POST | List or upload attachments |
/api/export_tickets.php |
GET | Export tickets to CSV/JSON |
/api/generate_api_key.php |
POST | Generate API key (admin) |
/api/revoke_api_key.php |
POST | Revoke API key (admin) |
/api/delete_comment.php |
POST | Delete comment (owner/admin) |
/api/update_comment.php |
POST | Update comment (owner/admin) |
/api/delete_attachment.php |
POST/DELETE | Delete attachment |
/api/download_attachment.php |
GET | Download attachment (visibility checked) |
/api/check_duplicates.php |
GET | Check for duplicate tickets |
/api/manage_recurring.php |
CRUD | Recurring tickets (admin) |
/api/manage_templates.php |
CRUD | Templates (admin) |
/api/manage_workflows.php |
CRUD | Workflow rules (admin) |
/api/custom_fields.php |
CRUD | Custom field definitions/values (admin) |
/api/saved_filters.php |
CRUD | Saved filter combinations |
/api/user_preferences.php |
GET/POST | User preferences |
/api/notifications.php |
GET/POST | In-app notifications (bell) |
/api/user_avatar.php |
GET | User avatar from lldap (cached JPEG) |
/api/audit_log.php |
GET | Audit log entries (admin) |
/api/watch_ticket.php |
POST | Watch/unwatch a ticket |
/api/health.php |
GET | Health check |
Project Structure
tinker_tickets/
├── api/
│ ├── add_comment.php # POST: Add comment
│ ├── assign_ticket.php # POST: Assign ticket to user
│ ├── audit_log.php # GET: Audit log entries (admin)
│ ├── bootstrap.php # Shared auth/setup include (not a public endpoint)
│ ├── bulk_operation.php # POST: Bulk operations (admin only)
│ ├── check_duplicates.php # GET: Check for duplicate tickets
│ ├── clone_ticket.php # POST: Clone an existing ticket
│ ├── custom_fields.php # CRUD: Custom field definitions/values (admin)
│ ├── delete_attachment.php # POST/DELETE: Delete attachment
│ ├── delete_comment.php # POST: Delete comment (owner/admin)
│ ├── download_attachment.php # GET: Download with visibility check
│ ├── export_tickets.php # GET: Export tickets to CSV/JSON
│ ├── generate_api_key.php # POST: Generate API key (admin)
│ ├── get_template.php # GET: Fetch ticket template
│ ├── get_users.php # GET: Get user list
│ ├── health.php # GET: Health check endpoint
│ ├── manage_recurring.php # CRUD: Recurring tickets (admin)
│ ├── manage_templates.php # CRUD: Templates (admin)
│ ├── manage_workflows.php # CRUD: Workflow rules (admin)
│ ├── notifications.php # GET/POST: In-app notification bell
│ ├── revoke_api_key.php # POST: Revoke API key (admin)
│ ├── saved_filters.php # CRUD: Saved filter combinations
│ ├── ticket_dependencies.php # GET/POST/DELETE: Ticket dependencies
│ ├── update_comment.php # POST: Update comment (owner/admin)
│ ├── update_ticket.php # POST: Update ticket (workflow validation)
│ ├── upload_attachment.php # GET/POST: List or upload attachments
│ ├── user_avatar.php # GET: LDAP avatar proxy with disk cache
│ ├── user_preferences.php # GET/POST: User preferences
│ └── watch_ticket.php # POST: Watch/unwatch a ticket
├── assets/
│ ├── css/
│ │ ├── base.css # LotusGuild Terminal Design System (copied from web_template)
│ │ ├── dashboard.css # Dashboard + terminal styling
│ │ └── ticket.css # Ticket view styling
│ ├── js/
│ │ ├── advanced-search.js # Advanced search modal
│ │ ├── ascii-banner.js # ASCII art banner (rendered in boot overlay on first visit)
│ │ ├── base.js # LotusGuild JS utilities — window.lt (copied from web_template)
│ │ ├── dashboard.js # Dashboard + bulk actions + kanban + sidebar + charts
│ │ ├── keyboard-shortcuts.js # Keyboard shortcuts (uses lt.keys)
│ │ ├── markdown.js # Markdown rendering + ticket linking (XSS-safe)
│ │ ├── settings.js # User preferences
│ │ ├── ticket.js # Ticket + comments + visibility + @mention
│ │ ├── toast.js # Deprecated shim (no longer loaded — all callers use lt.toast directly)
│ │ └── utils.js # escapeHtml (→ lt.escHtml), getTicketIdFromUrl, showConfirmModal
│ └── images/
│ └── favicon.png
├── config/
│ └── config.php # Config + .env loading
├── controllers/
│ ├── DashboardController.php # Dashboard with stats + filters
│ └── TicketController.php # Ticket CRUD + timeline + visibility
├── cron/
│ └── create_recurring_tickets.php # Process recurring ticket schedules
├── helpers/
│ ├── CacheHelper.php # File-based cache (stats, avatars)
│ ├── Database.php # Centralized mysqli connection
│ ├── NotificationHelper.php # Matrix hookshot webhook events
│ ├── SynapseHelper.php # Resolves usernames → Matrix IDs via Synapse admin API
│ └── UrlHelper.php # Canonical ticket URLs using APP_DOMAIN
├── middleware/
│ ├── ApiKeyAuth.php # Bearer token auth for external API (hwmonDaemon)
│ ├── AuthMiddleware.php # Authelia SSO integration
│ ├── CsrfMiddleware.php # CSRF protection
│ ├── RateLimitMiddleware.php # Session + IP-based rate limiting
│ └── SecurityHeadersMiddleware.php # CSP headers with per-request nonce generation
├── models/
│ ├── ApiKeyModel.php # API key generation/validation
│ ├── AuditLogModel.php # Audit logging + timeline
│ ├── BulkOperationsModel.php # Bulk operations tracking
│ ├── CommentModel.php # Comment data access
│ ├── CustomFieldModel.php # Custom field definitions/values
│ ├── DependencyModel.php # Ticket dependencies
│ ├── RecurringTicketModel.php # Recurring ticket schedules
│ ├── SavedFiltersModel.php # Saved filter combinations
│ ├── StatsModel.php # Dashboard statistics (cached)
│ ├── TemplateModel.php # Ticket templates
│ ├── TicketModel.php # Ticket CRUD + visibility + collision-safe IDs
│ ├── UserModel.php # User management + groups
│ ├── UserPreferencesModel.php # User preferences
│ └── WorkflowModel.php # Status transition workflows
├── scripts/
│ ├── add_closed_at_column.php # Migration: add closed_at column to tickets
│ ├── add_comment_updated_at.php # Migration: add updated_at column to ticket_comments
│ ├── cleanup_orphan_uploads.php # Clean orphaned uploads (run manually or via cron)
│ └── create_dependencies_table.php # Create ticket_dependencies table
├── uploads/ # File attachment storage
│ └── avatars/ # lldap avatar disk cache
├── views/
│ ├── admin/
│ │ ├── ApiKeysView.php # API key management
│ │ ├── AuditLogView.php # Audit log browser
│ │ ├── CustomFieldsView.php # Custom field management
│ │ ├── RecurringTicketsView.php # Recurring ticket management
│ │ ├── TemplatesView.php # Template management
│ │ ├── UserActivityView.php # User activity report
│ │ └── WorkflowDesignerView.php # Workflow transition designer
│ ├── CreateTicketView.php # Ticket creation with visibility
│ ├── DashboardView.php # Dashboard with kanban + sidebar + charts
│ ├── layout_footer.php # Shared footer (notification polling, boot sequence)
│ ├── layout_header.php # Shared header (nav, command palette, theme toggle)
│ └── TicketView.php # Ticket view with timeline, SLA, watcher avatars
├── .env # Environment variables (GITIGNORED)
├── create_ticket_api.php # External API endpoint (hwmonDaemon, API-key auth)
├── README.md # This file
└── index.php # Main router
Workflow States
Default Workflow
Open → Pending → In Progress → Closed
↑ ↑
└───────────┘
All states can transition to Closed (with comment). Closed tickets can be reopened to Open or In Progress.
Setup & Configuration
1. Environment Configuration
Copy the example file and edit with your values:
cp .env.example .env
nano .env
Required environment variables:
DB_HOST=your_db_host
DB_USER=your_db_user
DB_PASS=your_password
DB_NAME=ticketing_system
APP_DOMAIN=your.domain.example
TIMEZONE=America/New_York
Matrix notification variables (all optional):
# hookshot generic webhook URL — send events to Matrix room
MATRIX_WEBHOOK_URL=https://matrix.lotusguild.org/_hookshot/webhook/...
# Comma-separated Matrix user IDs to @mention on new tickets / status changes
MATRIX_NOTIFY_USERS=@jared:matrix.lotusguild.org,@ops:matrix.lotusguild.org
# Matrix homeserver domain (used to build Matrix user IDs from LLDAP usernames)
MATRIX_DOMAIN=matrix.lotusguild.org
# Synapse internal URL and admin token (used to resolve usernames → Matrix IDs for watcher DMs)
SYNAPSE_ADMIN_URL=http://10.10.10.29:8008
SYNAPSE_ADMIN_TOKEN=your_synapse_admin_token
# Optional: send Matrix notification on comments and/or assignments
MATRIX_NOTIFY_COMMENTS=0
MATRIX_NOTIFY_ASSIGNMENTS=1
LDAP/avatar variables (optional):
LDAP_ENABLED=true
LDAP_HOST=10.10.10.39
LDAP_PORT=3890
LDAP_BIND_DN=uid=tinker-tickets,ou=people,dc=example,dc=com
LDAP_BIND_PW=your_bind_password
LDAP_BASE_DN=dc=example,dc=com
LDAP_USER_BASE=ou=people,dc=example,dc=com
AVATAR_CACHE_TTL=3600
Note: APP_DOMAIN is required for Matrix webhook ticket links to work correctly. Without it, links will default to localhost.
2. Cron Jobs
Add to crontab for recurring tickets and optional cleanup:
# Run every hour to create scheduled recurring tickets
0 * * * * php /path/to/tinkertickets/cron/create_recurring_tickets.php
# Optional: clean up orphaned uploads weekly
0 3 * * 0 php /path/to/tinkertickets/scripts/cleanup_orphan_uploads.php
3. File Uploads
Ensure the uploads/ directory exists and is writable:
mkdir -p /path/to/tinkertickets/uploads/avatars
chown www-data:www-data /path/to/tinkertickets/uploads
chmod 755 /path/to/tinkertickets/uploads
4. Authelia Integration
Tinker Tickets uses Authelia for SSO. User information is passed via headers:
Remote-User: UsernameRemote-Name: Display nameRemote-Email: Email addressRemote-Groups: User groups (comma-separated)
Admin users must be in the admin group in LLDAP.
5. hwmonDaemon API Key
- Go to
/admin/api-keysand generate a new key named e.g. "hwmonDaemon" - Copy the displayed key (shown only once)
- On each monitored server, create
/etc/hwmonDaemon/.env:TICKET_API_KEY=your_generated_key TICKET_API_URL=http://10.10.10.45/create_ticket_api.php
Developer Notes
Key conventions and gotchas for working with this codebase:
- Session format:
$_SESSION['user']['user_id'](not$_SESSION['user_id']) - API auth check: Verify
$_SESSION['user']['user_id']exists - Admin check:
$_SESSION['user']['is_admin'] ?? false - Config path:
config/config.php(notconfig/db.php) - Comments table:
ticket_comments(notcomments) - CSRF: Required for all POST/DELETE requests via
X-CSRF-Tokenheader; bootstrap.php rotates token and returns it incsrf_tokenfield of allapiRespond()responses - Cache busting:
ASSET_VERSIONis auto-computed from asset file mtimes; override withASSET_VERSION=in.env - Ticket linking: Use
#123456789in markdown-enabled comments - User groups: Stored in
users.groupsas comma-separated values - API routing: All API endpoints must be registered in
index.phprouter - Session in APIs:
RateLimitMiddlewarestarts the session — guard subsequentsession_start()calls withif (session_status() === PHP_SESSION_NONE) - Database collation: Use
utf8mb4_general_ci(notunicode_ci) for new tables - Matrix URLs: Set
APP_DOMAINin.envfor correct ticket URLs in Matrix notifications - Ticket ID extraction: Use
getTicketIdFromUrl()helper in JS files - CSP nonces: All inline
<script>tags requirenonce="<?php echo $nonce; ?>" - Visibility validation: Internal visibility requires at least one group — validated server-side
- Rate limiting: Both session-based AND IP-based limits are enforced
- Relative timestamps: Dashboard dates use
lt.time.ago()and refresh every 60 s; full date is always in thetitleattribute for hover - Boot sequence: Shows ASCII banner then boot messages on first page visit per session (
sessionStorage.getItem('booted')); removed on subsequent loads - No raw
fetch(): All AJAX calls uselt.api.get/post/put/delete()— never use rawfetch(). The wrapper auto-addsContent-Type: application/jsonandX-CSRF-Token, auto-parses JSON, and throws on non-2xx. - Confirm dialogs: Never use browser
confirm(). UseshowConfirmModal(title, message, type, onConfirm)(defined inutils.js, available on all pages). Types:'warning'|'error'|'info'. utils.json all pages:utils.jsis loaded by all views (including admin). It providesescapeHtml(),getTicketIdFromUrl(), andshowConfirmModal().- No
toast.js:toast.jsis deprecated and no longer loaded by any view. Uselt.toast.success/error/warning/info()directly frombase.js. - Stats cache:
StatsModelcaches stats for 60 s. Any API that modifies ticket state must call(new StatsModel($conn))->invalidateCache()after changes (bulk_operation, assign_ticket, update_ticket, clone_ticket all do this). - External API (
create_ticket_api.php): UsesApiKeyAuth(Bearer token), not session auth. Served directly by the web server from the document root — not through the index.php router. Includes deduplication logic to prevent duplicate hw-alert tickets within 24 h.
File Reference
| File | Purpose |
|---|---|
index.php |
Main router for all routes |
create_ticket_api.php |
External API (hwmonDaemon) — Bearer token auth, deduplication |
config/config.php |
Config loader + .env parsing |
api/update_ticket.php |
Ticket updates with workflow + visibility validation |
api/notifications.php |
In-app notification bell — reads from audit_log |
api/user_avatar.php |
LDAP avatar proxy with disk cache |
api/download_attachment.php |
File downloads with visibility check |
api/bulk_operation.php |
Bulk operations with visibility filtering |
models/TicketModel.php |
Ticket CRUD, visibility filtering, collision-safe ID generation |
models/ApiKeyModel.php |
API key generation and validation |
models/StatsModel.php |
Dashboard statistics (60 s cache; invalidated on ticket changes) |
middleware/ApiKeyAuth.php |
Bearer token authentication for external API |
middleware/AuthMiddleware.php |
Authelia header parsing + session setup |
middleware/CsrfMiddleware.php |
CSRF token generation and validation |
middleware/SecurityHeadersMiddleware.php |
CSP headers with per-request nonce generation |
middleware/RateLimitMiddleware.php |
Session + IP-based rate limiting |
helpers/NotificationHelper.php |
Matrix hookshot webhook events |
helpers/SynapseHelper.php |
Username → Matrix ID resolution via Synapse admin API |
assets/js/dashboard.js |
Dashboard UI, kanban, sidebar, bulk actions, charts, command palette |
assets/js/ticket.js |
Ticket UI, @mention autocomplete, lightbox, visibility editing |
assets/js/markdown.js |
Markdown parsing + ticket linking (XSS-safe) |
assets/css/dashboard.css |
Terminal styling, kanban, sidebar, charts, workload panel |
assets/css/ticket.css |
Ticket view: SLA progress, attachment thumbnails, timeline |
Security Implementations
| Feature | Implementation |
|---|---|
| SQL Injection | All queries use prepared statements with parameter binding |
| XSS Prevention | HTML escaped in markdown parser; CSP with per-request nonces |
| CSRF Protection | Token-based with constant-time comparison (hash_equals); rotated on each write |
| Session Security | Fixation prevention, secure cookies, session timeout |
| Rate Limiting | Session-based + IP-based (file storage) |
| File Security | Path traversal prevention, MIME type validation, uploads .htaccess blocks execution |
| Visibility | Enforced on ticket views, downloads, and bulk operations |
| API Key Auth | SHA-256 hashed keys stored in DB; Bearer token auth for external API |
License
Internal use only - LotusGuild Infrastructure