JS was toggling .collapsed on the wrong element (dashboardSidebar div instead of lt-sidebar aside), and the expand button was permanently display:none. When collapsed, users had no way to re-expand. - toggleSidebar now targets lt-sidebar (the aside) - Toggle button flips ◀ ↔ ▶ to indicate state and serve as the expand button - Collapsed CSS hides the body and label, centers the ▶ button in the strip - Remove the dead sidebarExpandBtn element from HTML - Persist and restore state correctly on page load 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