Server-side fixes

Fix API and validation bugs without a deploy. Run the sidecar next to your app, then add the Sarcio library for your language.

1. Run the sidecar

The sidecar runs on the same host as your app and hands it approved fixes over a local socket. It only makes outbound HTTPS requests to control.sarcio.io. If it is down, your app runs its normal code.

Docker Compose

Add the service and share its socket directory with your app. Pin a version: there is no latest tag.

compose.yaml

services:  sarcio-sidecar:    image: ghcr.io/sarcioai/sarcio-sidecar:0.2.2    command:      - "-socket"      - "/run/sarcio/sarcio.sock"      - "-control-plane"      - "https://control.sarcio.io"      - "-site"      - "pk_your_site"    environment:      SARCIO_CONTROL_PLANE_KEY: sk_your_sidecar_key      SARCIO_SHIM_TOKEN: an-app-token-you-choose      SARCIO_SOCKET_MODE: "0660" # if your app runs as a different user    volumes:      - sarcio-run:/run/sarcio
  your-app:    volumes:      - sarcio-run:/run/sarcio # the same socket directory
volumes:  sarcio-run:

Linux: install script, deb or rpm

The script picks the deb, rpm or binary for the machine and verifies it before installing. Read it first.

Shell

curl -fsSLO https://github.com/sarcioai/sarcio-sidecar-releases/releases/latest/download/install.shless install.shsh install.sh v0.2.2

Set your keys in /etc/sarcio/sidecar.env:

/etc/sarcio/sidecar.env

SARCIO_CONTROL_PLANE=https://control.sarcio.ioSARCIO_SITE_KEY=pk_your_siteSARCIO_CONTROL_PLANE_KEY=sk_your_sidecar_keySARCIO_SHIM_TOKEN=an-app-token-you-choose

Add your app's user (here www-data) to the sarcio group so it can reach the socket, then start the service:

Shell

sudo usermod -aG sarcio www-datasudo systemctl enable --now sarcio-sidecar

Packages and binaries are also on the releases page.

Add the sidecar as a second container in the pod and share the socket over an emptyDir volume:

deployment.yaml (pod spec)

spec:  volumes:    - name: sarcio-run      emptyDir: {}  containers:    - name: your-app      volumeMounts:        - { name: sarcio-run, mountPath: /run/sarcio }    - name: sarcio-sidecar      image: ghcr.io/sarcioai/sarcio-sidecar:0.2.2      args:        - "-socket"        - "/run/sarcio/sarcio.sock"        - "-control-plane"        - "https://control.sarcio.io"        - "-site"        - "pk_your_site"      env:        - { name: SARCIO_SOCKET_MODE, value: "0660" }      envFrom:        - secretRef: { name: sarcio-sidecar } # SARCIO_CONTROL_PLANE_KEY, SARCIO_SHIM_TOKEN      volumeMounts:        - { name: sarcio-run, mountPath: /run/sarcio }

Each release's SHA256SUMS is signed with minisign. The install script checks it when minisign is installed. By hand:

Shell

minisign -Vm SHA256SUMS -P RWT/8gXBswXCZXMhfResaZIVGcwEjR/89MHEoYpo0C5ejHNUVTWiddoDsha256sum --check --ignore-missing SHA256SUMS

The container image is signed with cosign. Save this public key as sarcio-cosign.pub, verify, and pin the digest it prints rather than a tag:

sarcio-cosign.pub

-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0BZVOjeZFPL6TDrSNwB7nEwnencpxBbo2v/0aOF+3+jt9faYKQVP4I55/QSNvEKzEdIORD6Xr7R/HU3Q46LC3g==-----END PUBLIC KEY-----

Shell

cosign verify --key sarcio-cosign.pub ghcr.io/sarcioai/sarcio-sidecar:0.2.2

2. Add the Sarcio library to your app

One small dependency from your language's public registry. It asks the local sidecar, per request, whether a fix applies. Use the same site key, socket path and app token you gave the sidecar.

Node.js

The Express adapter needs Express 5.

Shell

npm install @sarcio/middleware

Express

Create one client per process and register it after your body parser:

server.ts

import { createProtocolShim, ProtocolShimClient } from '@sarcio/middleware';import express from 'express';
const sarcio = new ProtocolShimClient({  siteKey: 'pk_your_site',  shimToken: process.env.SARCIO_SHIM_TOKEN,  target: { socketPath: '/run/sarcio/sarcio.sock' }});await sarcio.start();
const app = express();app.use(express.json());app.use(createProtocolShim(sarcio)); // after your body parser

A fix that relaxes a rule or changes a setting reaches your handler on req.sarcio.skipValidations and req.sarcio.config.

Create the client as above, then register one line:

Fastify

import { createProtocolFastifyHook } from '@sarcio/middleware';
app.addHook('preHandler', createProtocolFastifyHook(sarcio));

Koa

import { createProtocolKoaMiddleware } from '@sarcio/middleware';
app.use(createProtocolKoaMiddleware(sarcio)); // after your body parser

Hono

import { createProtocolHonoMiddleware } from '@sarcio/middleware';
app.use('*', createProtocolHonoMiddleware(sarcio));

node:http

import { createServer } from 'node:http';import { withProtocolSarcio } from '@sarcio/middleware';
createServer(withProtocolSarcio(sarcio, handler)).listen(3000);

PHP

Shell

composer require sarcio/shim:^0.2

Laravel

The service provider registers itself. Set the connection in .env:

.env

SARCIO_SIDECAR_DSN=unix:///run/sarcio/sarcio.sockSARCIO_SITE_KEY=pk_your_siteSARCIO_SHIM_TOKEN=an-app-token-you-choose

Add the middleware to the global stack (Laravel 11 and later):

bootstrap/app.php

// bootstrap/app.php->withMiddleware(function (Middleware $middleware) {    $middleware->append(\Sarcio\Shim\Laravel\SarcioMiddleware::class);})

Your validation code reads what an approved fix relaxed:

PHP

$sarcio = $request->attributes->get('sarcio');
if (!in_array('referralCodeRequired', $sarcio['skipValidations'] ?? [], true)) {    // enforce the rule as normal}

Laravel 10: app/Http/Kernel.php

// app/Http/Kernel.phpprotected $middleware = [    // ...    \Sarcio\Shim\Laravel\SarcioMiddleware::class,];

Symfony: config/services.yaml

# config/services.yamlSarcio\Shim\Cache\ApcuRouteSetCache: ~
Sarcio\Shim\Shim:  arguments:    $dsn: '%env(SARCIO_SIDECAR_DSN)%'    $siteKey: '%env(SARCIO_SITE_KEY)%'    $cache: '@Sarcio\Shim\Cache\ApcuRouteSetCache'    $framework: 'symfony'    $shimToken: '%env(SARCIO_SHIM_TOKEN)%'
Sarcio\Shim\Symfony\SarcioKernelListener:  arguments:    $shim: '@Sarcio\Shim\Shim'  tags:    - { name: kernel.event_listener, event: kernel.request }    - { name: kernel.event_listener, event: kernel.response }

Plain PHP

$sarcio = new \Sarcio\Shim\Shim(    dsn: 'unix:///run/sarcio/sarcio.sock',    siteKey: 'pk_your_site',    cache: new \Sarcio\Shim\Cache\ApcuRouteSetCache(),    shimToken: getenv('SARCIO_SHIM_TOKEN') ?: '',);
// null means: run your normal code$fix = $sarcio->evaluate($method, $path, ['headers' => $headers, 'body' => $body]);

On PHP-FPM, install the APCu extension so requests to unpatched routes skip the sidecar.

Java

Needs Java 21. The library is on Maven Central.

pom.xml

<dependency>  <groupId>io.sarcio</groupId>  <artifactId>shim</artifactId>  <version>0.2.0</version></dependency>

Spring Boot

The filter registers itself at the front of the chain. Configure it:

application.properties

sarcio.enabled=truesarcio.socket-path=/run/sarcio/sarcio.socksarcio.site-key=pk_your_sitesarcio.shim-token=${SARCIO_SHIM_TOKEN}

Your handlers read what an approved fix relaxed. Set sarcio.enabled=false to switch the filter off.

Java

@SuppressWarnings("unchecked")var sarcio = (Map<String, Object>) request.getAttribute(SarcioFilter.ATTRIBUTE);var skip = (List<String>) sarcio.get("skipValidations");

build.gradle.kts

dependencies {  implementation("io.sarcio:shim:0.2.0")}

Without Spring, register the filter yourself:

Plain servlets

var sarcio = SarcioShim.create(    ProtocolClient.Target.unix("/run/sarcio/sarcio.sock"),    "pk_your_site", "java", 25,    System.getenv().getOrDefault("SARCIO_SHIM_TOKEN", ""));sarcio.start();
servletContext.addFilter("sarcio", new SarcioFilter(sarcio))    .addMappingForUrlPatterns(null, false, "/*");

WordPress

One plugin covers front-end and server-side fixes. No script tag, no Composer.

  • Install Sarcio from Plugins → Add New and activate it.
  • Open Settings → Sarcio and enter your site key and the API URL https://control.sarcio.io. Reporting and front-end fixes now work, with no sidecar.
  • For server-side fixes, run the sidecar on the host and add its connection to wp-config.php:

wp-config.php

define('SARCIO_SITE_KEY', 'pk_your_site');define('SARCIO_API_URL', 'https://control.sarcio.io');define('SARCIO_SIDECAR_DSN', 'unix:///run/sarcio/sarcio.sock');define('SARCIO_SHIM_TOKEN', 'an-app-token-you-choose');

SettingWhat it does
Reporter audienceWho sees the reporter: users who can edit_posts (default), logged_in or everyone. Fixes always apply to every visitor.
SARCIO_SERVER_SCOPEall (default), rest for the REST API only, or off for front-end fixes only.

A value set in wp-config.php shows read-only in the admin.

3. Check it works

The sidecar logs listening on unix /run/sarcio/sarcio.sock when it is up:

Shell

# Docker Composedocker compose logs sarcio-sidecar
# deb / rpmjournalctl -u sarcio-sidecar -n 20curl -s http://127.0.0.1:9090/healthz

Then report a bug on a server-backed form and run your first fix. The fix applies on the next request after approval.

Rotating the sidecar key

Issue a new key, deploy it, and the old one keeps working for seven days. A site with no active key gets no server-side fixes.

Ready to try it on your own site?

Create a workspace, add a site, and follow these steps on your own app.

Create a workspace