Overview
Bolts are the CartOS extension system — modular add-ons that extend platform functionality without modifying core code. Think of them as apps or plugins that can inject snippets into theme hook points, register their own storefront pages, add admin interfaces, and respond to platform events.
There are two categories of bolts:
- Core bolts are built and maintained by CartOS (Salloq). They live in
/includes/bolts/core/and ship free with the platform. - Vendor bolts are built by third-party developers. They live in
/includes/bolts/vendor/{vendor-handle}/{bolt-handle}/and are distributed through the Bolt Store.
/includes/bolts/. The admin router at /admin/bolts/ reads from this directory — you never need to place files in both locations.Directory Structure
Manifest File
The manifest.json file is the heart of every bolt. It defines metadata, hooks, routes, settings, and admin pages. Here's a complete example:
{
"name": "Shipping Protection",
"handle": "shipping-protection",
"version": "1.0.0",
"description": "Offer shipping protection at checkout.",
"author": {
"name": "Salloq",
"email": "support@salloq.com",
"url": "https://salloq.com"
},
"icon": "assets/icon.svg",
"category": "operations",
"hooks": {
"cart_after_items": {
"snippet": "shipping-protection",
"priority": 10
},
"head": {
"asset": "styles.css",
"priority": 5
}
},
"routes": [
{
"path": "protection/claim",
"template": "claim-form",
"methods": ["GET", "POST"],
"title": "File a Claim"
}
],
"settings": {
"sections": [
{
"title": "General",
"settings": [
{ "id": "enabled", "type": "checkbox", "label": "Enable", "default": true },
{ "id": "price", "type": "number", "label": "Protection Price", "default": 1.99, "min": 0, "step": 0.01 }
]
}
]
},
"admin_pages": [
{
"title": "Claims",
"path": "admin/index.php",
"icon": "shield"
}
]
}
Hook System
Hooks are the primary way bolts inject content into the storefront. When a theme includes {% bolt_hook 'cart_after_items' %}, CartOS checks all installed bolts for matching hook definitions and renders their snippets in priority order (lower numbers first).
Available Theme Hooks
| Hook Name | Location | Common Use |
|---|---|---|
head | Inside <head> | CSS, meta tags, analytics scripts |
body_end | Before </body> | JavaScript, chat widgets, modals |
cart_before_items | Before cart item loop | Cart messaging, announcements |
cart_after_items | After cart item loop | Upsells, shipping protection |
cart_before_checkout | Before checkout button | Final offers, terms acceptance |
product_meta | After product price | Badges, ratings, trust seals |
product_tabs | Product description area | Reviews, spec sheets, FAQs |
Hook Types
There are two ways to attach content to a hook:
// Inject a snippet (renders a Liquid template from snippets/)
"hooks": {
"cart_after_items": {
"snippet": "my-upsell-widget",
"priority": 10
}
}
// Inject an asset (loads a CSS or JS file)
"hooks": {
"head": {
"asset": "styles.css",
"priority": 5
}
}
Settings
Bolt settings are configured by merchants in the admin panel. They're defined in the manifest's settings object and automatically rendered as a settings form.
Setting Types
| Type | Input | Properties |
|---|---|---|
text | Text input | default, placeholder |
checkbox | Boolean toggle | default (true/false) |
select | Dropdown | options (array of {value, label}), default |
number | Numeric input | default, min, max, step |
image | File upload | Shows preview |
color | Color picker | default (hex) |
"settings": {
"sections": [
{
"title": "General",
"settings": [
{ "id": "enabled", "type": "checkbox", "label": "Enable bolt", "default": true },
{ "id": "mode", "type": "select", "label": "Mode",
"options": [
{ "value": "test", "label": "Test" },
{ "value": "production", "label": "Production" }
],
"default": "test"
}
]
},
{
"title": "Appearance",
"settings": [
{ "id": "accent_color", "type": "color", "label": "Accent color", "default": "#3584e4" },
{ "id": "logo", "type": "image", "label": "Custom logo" }
]
}
]
}
Frontend Routes
Bolts can register their own storefront pages without modifying core routing. Routes are declared in the manifest and mapped to templates and optional API handlers.
"routes": [
{
"path": "affiliate/login",
"template": "login",
"methods": ["GET", "POST"],
"title": "Affiliate Login"
},
{
"path": "affiliate/dashboard",
"template": "dashboard",
"auth": "affiliate"
}
]
When a customer visits /affiliate/login, CartOS renders templates/login.html from your bolt directory. If the route accepts POST and a matching file exists in api/, it handles the form submission.
"auth" to require login before accessing a route. The value specifies the user type (e.g., "affiliate", "customer").Admin Pages
Bolts with an admin/ subdirectory automatically get a "Manage" button in the admin Bolt settings. The admin page is loaded via the /admin/bolts/{handle}/ URL.
"admin_pages": [
{
"title": "Manage Claims",
"path": "admin/index.php",
"icon": "shield"
}
]
Admin Page Bootstrapping
Your admin PHP file needs to include the platform's init file. Use dirname(__DIR__, 5) to navigate from the bolt's admin directory to the site root:
<?php
// Navigate from /includes/bolts/core/{handle}/admin/ to site root
require_once dirname(__DIR__, 5) . '/includes/init.php';
// Your admin logic here
$claims = $db->query("SELECT * FROM shipping_claims ORDER BY created_at DESC");
?>
<div class="bolt-admin">
<h2>Shipping Protection Claims</h2>
<!-- Your admin UI -->
</div>
The admin BoltManager.php detects the admin/ directory automatically:
// Detection: if admin/ directory exists, show "Manage" button
$hasAdmin = is_dir($corePath . '/' . $dir . '/admin');
Templates & Snippets
Bolt templates and snippets use the same Liquid syntax as themes. Templates are full-page views used by routes, while snippets are partial fragments injected at hook points.
<div class="upsell-widget">
<h4>You might also like</h4>
{% for product in recommendations %}
<div class="upsell-item">
<img src="{{ product.featured_image | image_url: width: 100 }}">
<span>{{ product.title }}</span>
<span>{{ product.price | money }}</span>
</div>
{% endfor %}
</div>
Assets
Place CSS and JavaScript files in the assets/ directory. Link them to hooks in the manifest to load them on the storefront:
// Load CSS via the head hook
"head": { "asset": "styles.css", "priority": 5 }
// Load JS via the body_end hook
"body_end": { "asset": "scripts.js", "priority": 90 }
Database Migrations
Bolts that need their own database tables should include SQL migration files in the migrations/ directory. Migrations are run during bolt installation.
CREATE TABLE IF NOT EXISTS shipping_claims (
id INT AUTO_INCREMENT PRIMARY KEY,
order_id INT NOT NULL,
customer_id INT NOT NULL,
status ENUM('pending', 'approved', 'denied') DEFAULT 'pending',
reason TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX (order_id),
INDEX (customer_id)
);
Testing Your Bolt
During development, place your bolt in /includes/bolts/core/{handle}/ on a test store. CartOS will automatically detect it and list it in the admin under Bolts.
- Verify the manifest loads correctly in the admin bolt list.
- Check that hook snippets render in the correct theme locations.
- Test any registered routes at their expected URLs.
- Confirm admin pages load and function properly.
- Run database migrations on a fresh install.
- Test with multiple themes to ensure compatibility.
Publishing to the Bolt Store
To publish a bolt to the CartOS Bolt Store at bolt.cart-os.com:
- Ensure your manifest includes all required fields:
name,handle,version,description,author, andicon. - Connect your GitHub repository to your developer account.
- Tag a release with a semantic version matching your manifest version.
- Submit for review through the developer portal.
- Our team will test installation, compatibility with default themes, and security before approving.