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.
🛈
Single Source of Truth: All bolt code lives under /includes/bolts/. The admin router at /admin/bolts/ reads from this directory — you never need to place files in both locations.

Directory Structure

/includes/bolts/ ├── BoltManager.php # Storefront bolt manager ├── BoltBaseController.php ├── core/ # Core (free) bolts │ └── {bolt-handle}/ │ ├── manifest.json # Bolt definition (required) │ ├── api/ # API handlers for routes & POST actions │ │ ├── login.php │ │ └── register.php │ ├── templates/ # Frontend route templates │ │ ├── login.html │ │ └── dashboard.html │ ├── snippets/ # Hook injection snippets │ │ └── cart-upsell.html │ ├── assets/ │ │ ├── styles.css │ │ └── scripts.js │ ├── migrations/ # SQL migration files │ └── admin/ │ └── index.php # Admin UI for this bolt └── vendor/ # Third-party bolts └── {vendor-handle}/ └── {bolt-handle}/ └── (same structure as core)

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:

manifest.json
{
  "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 NameLocationCommon Use
headInside <head>CSS, meta tags, analytics scripts
body_endBefore </body>JavaScript, chat widgets, modals
cart_before_itemsBefore cart item loopCart messaging, announcements
cart_after_itemsAfter cart item loopUpsells, shipping protection
cart_before_checkoutBefore checkout buttonFinal offers, terms acceptance
product_metaAfter product priceBadges, ratings, trust seals
product_tabsProduct description areaReviews, 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

TypeInputProperties
textText inputdefault, placeholder
checkboxBoolean toggledefault (true/false)
selectDropdownoptions (array of {value, label}), default
numberNumeric inputdefault, min, max, step
imageFile uploadShows preview
colorColor pickerdefault (hex)
Example: Settings with sections
"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.

🛈
Authentication: Set "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:

admin/index.php
<?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.

snippets/cart-upsell.html
<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.

migrations/001_create_claims_table.sql
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.

  1. Verify the manifest loads correctly in the admin bolt list.
  2. Check that hook snippets render in the correct theme locations.
  3. Test any registered routes at their expected URLs.
  4. Confirm admin pages load and function properly.
  5. Run database migrations on a fresh install.
  6. 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:

  1. Ensure your manifest includes all required fields: name, handle, version, description, author, and icon.
  2. Connect your GitHub repository to your developer account.
  3. Tag a release with a semantic version matching your manifest version.
  4. Submit for review through the developer portal.
  5. Our team will test installation, compatibility with default themes, and security before approving.
⚠️
Review Guidelines: Bolts must not reference competitor product names in descriptions or code. All database operations should use parameterized queries. Admin pages must include proper authentication checks.
Tip: Study existing core bolts like Shipping & Fulfillment, Ratings & Reviews, and SEO as reference implementations. They demonstrate all the patterns described above.