Development and Contributing Guide
First off, thank you for considering contributing to ChroGPS Dash! It’s people like you who make open-source tools better for the entire NTP/GNSS/precision time-keeping community. Before diving in, familiarize yourself with the Architecture page - it explains how the file is structured, how requests are routed, and how the frontend and backend interact.
Core Philosophy: “Zero Dependencies”
This project adheres to a strict no-dependency philosophy. All contributions must respect these constraints:
- No external CSS frameworks (Bootstrap, Tailwind, etc.)
- No external JS libraries (jQuery, Chart.js, Highcharts, etc.)
- No package managers (npm, composer, pip)
- Single-file deployment - the entire dashboard logic resides in
index.php - Native APT packages only - use programs native to Debian/Debian-like systems for portability and repeatability
Pull Requests
Before submitting a pull request:
- Fork the repository and create your branch from
master. - Test locally - ensure your changes work on a standard Debian/Ubuntu stack with
chronyandgpsd. - Responsive check - verify the UI layout on both Desktop (side-by-side row layout) and Mobile (stacked single column). Also verify collapsible row bars appear in “All Views” and are hidden on mobile and in single-row views.
- Theme check - toggle Light/Dark mode to ensure all new elements use the correct CSS variables.
- Submit a Pull Request with a clear description of your changes.
Reporting Bugs & Feature Requests
- Disabled and disregarded/dismissed: I only accept code contributions and bugfixes directly via Pull Requests.
- Did I make it clear that only code contributions and bugfixes are accepted? 😉
Development Guidelines
PHP & Graphing (The “Native SVG” Engine)
I do not use client-side graphing libraries. All graphs are generated server-side using native PHP to output raw SVG XML.
- Draw Function: Use the
drawGraphSVG()helper function for all standard dual-axis line graphs. Its signature:drawGraphSVG($data, $labelY1, $labelY2, $unitY1, $unitY2, $colorY2 = 'var(--green)', $zeroBase = false, $staticLabel2 = null)$colorY2sets the primary (left-axis) line color using a CSS variable token. Note: despite theY2suffix in the parameter name, this controls the primary/left-axis series color – not the secondary axis. This is a known naming inconsistency in the codebase. The secondary (right-axis) line always usesvar(--accent). Pass$zeroBase = trueto force the Y origin to zero (used for frequency graphs). Pass a string to$staticLabel2to suppress the secondary line and use a static legend label instead. - Custom Multi-Series SVG: When a graph requires more than two data series (e.g., four DOP lines or per-constellation SNR lines), write a dedicated PHP function that outputs raw SVG directly rather than using
drawGraphSVG(). Custom SVG functions must match the standard graph dimensions (viewBox="0 0 1200 300",$px = 95,$py = 35) for visual consistency. They must also include:- Transparent
<rect>hit zones withclass='graph-hit-zone'anddata-time,data-l1/data-v1/data-c1…data-l7/data-v7/data-c7attributes to drive the shared JS tooltip (up to 7 series supported). - A
<div class='graph-legend-compact'>legend below the SVG using the.legend-series-color+style='--legend-color:var(--token)'pattern for series labels, with per-series avg/min/max popups. - Area fills at 4% opacity as a background layer, with polylines rendered on top.
- Grid lines and axis labels matching the
drawGraphSVG()style (5 steps,var(--border)stroke,stroke-dasharray='5,5').
- Transparent
- Rolling Log Files: New history graphs that require their own log must define a
define('FOO_LOG_FILE', '/var/tmp/cgpsd-foo.data')constant and use the samefopen('c+')/flock(LOCK_EX)/ read-append-trim-write rolling buffer pattern used by the existing log writers. The trim limit isSAT_LOG_MAX_LINES(15,000 lines). Register new log files in the admin init block’s$dataFilesarray so the admin panel can create them on first run. - Progress Bars: Use
getProgressBar($percent, $text)to render a themed progress bar. It automatically applies.p-bar-ok,.p-bar-warn, or.p-bar-errCSS classes based on the percentage value (>70% → warn, >90% → err), ensuring correct themed colors viavar(--status-ok/warn/err). Never build progress bar HTML by hand. - Tooltips: If you modify the graphs, ensure you preserve the invisible
<rect>“hit zones” that drive the JavaScript tooltips. - Log Parsing: The dashboard reads directly from
/var/log/chrony. Ensure any new parsers handle file permissions gracefully and fail silently if logs are missing.
JavaScript (Vanilla Only)
- No Frameworks: Use standard ES6+ JavaScript.
- AJAX Loop: The dashboard uses a single
fetch('?ajax=1')loop to update all data.- Do not add separate polling intervals for different components.
- All dynamic data should be returned in the single JSON response.
- The main poll response is a JSON object. Top-level keys include
sources,tracking,gps,graphs, andservice_status. If you add a new data field, add it to this response object – do not create a separate endpoint. - Other endpoints exist for specific actions (
?ajax=version,?ajax=clients,?ajax=get_settings,?ajax=save_settings,?ajax=regen_admin_token,?ajax=do_update,?ajax=purge_logs,?ajax=restart_gpsd,?ajax=restart_chrony,?ajax=restart_poller,?ajax=chrony_makestep). Do not repurpose these; add new action keys only when the operation is genuinely distinct from the main poll.
- DOM Manipulation: Use standard
document.getElementById()orquerySelector().
CSS & Theming
- Variables: Always use the defined CSS variables (e.g.,
--bg,--text,--accent,--green) to ensure full Dark/Light mode compatibility. - Row Layout:
- Mobile First: At
max-width: 899px,.row-cards-innerswitches toflex-direction: columnso all cards stack vertically. Collapse bars are hidden on mobile. - Desktop: The
.dashboardis a flex column of.dash-rowelements (created byinitRowLayout()in JavaScript). Within each row,.row-cards-innerlays cards out side-by-side withdisplay: flex. - Full Width Rows: Rows containing a single full-width card (SNR, history) use the
.dash-row-fullmodifier class, which gives the cardflex: 1 1 100%inside.row-cards-inner.
- Mobile First: At
- UI Styles: ChroGPS Dash has a specific and well-established set of styles, color palettes, etc. See the UI and Design Guidelines for the complete reference that all contributors should follow.
General Development Styles and Guidelines
- Code should be well-documented/commented
- No manual minification. The source file must remain fully human-readable. ChroGPS Dash includes a self-minifier that automatically strips comments, collapses whitespace, and removes redundant syntax from the HTML/CSS/JS output buffer at request time – the source is never modified. Never manually minify, uglify, or compress code you contribute; the minifier handles that transparently.
- PHP code follows PSR-12 guidelines
- JavaScript follows Modern Vanilla JS (ES6+) guidelines.
- Indentation should be 4 spaces to maintain consistency with the PHP file structure.
- CSS and HTML are v3 and v5, respectively.
- Indentation should be 4 spaces to maintain consistency with the PHP file structure.
Hardware Support
- GPS Types: When parsing
gpsddata, aim to support generic NMEA devices as well as specific drivers (u-blox, SiRF). - Baud Rates: Preserve the display format
@ /dev/path (nnnn baud).
Adding New Settings and Configuration Options
ChroGPS Dash uses a single canonical block – $defaultConfig near the very top of index.php – as the source of truth for every user-configurable setting. Understanding how this block works is essential before adding any new option.
How $defaultConfig Works
$defaultConfig is a PHP string that contains the complete, correctly-formatted default contents of cgpsd-settings.php. It serves three purposes simultaneously:
- Fresh install: If
cgpsd-settings.phpdoes not exist, the dashboard writes$defaultConfigdirectly to disk to create it. - Shell installer migration:
install.shreads$defaultConfigdirectly from the downloadedindex.phpand injects any blocks whose variable is absent from the user’s existing settings file. No separate per-setting maintenance in the installer is required. - Web updater migration (Step 6): The web updater parses
$defaultConfigat runtime, extracts every setting block, and injects any that are absent from the user’s livecgpsd-settings.php. This means adding a setting to$defaultConfigis all that is required – the web updater picks it up automatically on the next update for every existing installation worldwide.
The block lives here in index.php:
$defaultConfig = '<?php
// --- OPTIONAL CONFIGURATION ---
// CHECK FOR UPDATES (True/False)
// ...
$CHECK_UPDATES = true;
// DISPLAY RESOLUTION (Sampling Interval in Seconds)
// ...
$GRAPH_SAMPLE_SEC = 30;
// ... (one blank line between each setting block) ...
// --- END OPTIONAL CONFIGURATION ---
';
Rules for Adding a New Setting
Follow all of these rules precisely. The web updater’s parser depends on this structure.
1. Add the block to $defaultConfig.
Each setting is a self-contained block consisting of one or more comment lines immediately followed by a single assignment line. Blocks are separated by exactly one blank line.
// SETTING NAME (Type)
// Description line one.
// Description line two (optional).
$YOUR_NEW_VAR = <default_value>;
Add it before the // --- END OPTIONAL CONFIGURATION --- line. Increment the number to follow the existing sequence. Use the same escape style as the surrounding single-quoted string – single quotes inside the string must be escaped as \'.
2. No additional changes to install.sh are needed.
The installer’s write_default_config and migrate_settings_from_php functions both read $defaultConfig directly from the downloaded index.php at runtime. There is no separate settings list to maintain in the installer. Adding the block to $defaultConfig is the only step required for the installer to handle the new setting correctly on fresh installs and upgrades alike.
3. Consume the variable in index.php.
Use isset() before referencing your new variable anywhere in the dashboard logic. Settings files from older installations will not have it until the next web update or installer run, so always guard against it being undefined:
// Safe - works whether or not the setting has been injected yet
if (isset($YOUR_NEW_VAR) && $YOUR_NEW_VAR === true) {
// feature logic
}
4. Document it in your Pull Request.
In your Pull Request, you must create a markdown description of the new setting so that I can update the configuration section of the ChroGPS Dash web page. Follow the same structure as existing entries: variable name as the heading, default value, description, and function (true/false behavior or accepted values).
You also must include the new setting in the example default settings file. Follow the same structure as existing entries in the example.
You can add the documentation directly in the body/comment section of the pull request, and it must be in raw/enclosed Markdown format.
The $skipVars Protected List
The web updater’s Step 6 migration maintains a small $skipVars array of variable names that are never injected during a web update, regardless of whether they are present in the user’s settings file:
$skipVars = ['ADMIN_TOKEN', 'UPDATE_TOKEN'];
ADMIN_TOKEN is protected because anyone running the web updater already has a valid token – injecting a new empty one would lock them out of the admin panel and break web updates entirely. UPDATE_TOKEN is the legacy name for the same credential and is kept in the list for backward compatibility with older installations. Do not add new settings to $skipVars unless there is a specific reason the web updater must never touch them. Variables that need special generation logic at install time (like tokens or secrets) are the only legitimate candidates.
Checklist: Adding a New Setting
- Block added to
$defaultConfiginindex.phpwith correct numbering and formatting - No installer changes needed -
install.shreads$defaultConfigfrom the downloadedindex.phpat runtime - Variable consumed with
isset()guard everywhere it is used in dashboard logic - Documented in the configuration reference
- Default value is the most conservative/safe option (features default to
false/off)
UI and Design Guidelines
The visual design system has been extracted to its own dedicated page. See UI and Design Guidelines for the complete reference, covering:
- All twenty-one themes and the
data-thememodel - Standalone page theming (dashboard gate and setup error page)
- The full color palette – primitive tokens, semantic tokens, and how to add new colors
- Typography, badge components, constellation chips, info tags, and utility classes
- Tooltip system (
has-popupanddata-tip) - Dashboard view and card system
- PHP-generated colors
- CSS authoring rules