Go and C++ HTTP
Run Larkfield's pricing service in Go with chi and in C++ with Drogon, with every quote admitted by the kernel before the handler runs.
On this page
A pricing service written in Go or C++ keeps its routes, validation, and storage. Chio wraps the handler, decides whether a quote request may reach it, and signs the decision, in the same contract the Node and Python services follow.
Starts from chio new pricing-go-cpp --example pricing-go-cpp. Both implementations serve POST /v1/quote and GET /v1/quotes on Larkfield's price table. The middleware architecture shows where the service and the sidecar meet.
Keep the service and add one authority boundary
The kernel does not take over the pricing logic. The Go and C++ handlers compute the price in exact cents and record the quote; Chio decides, before either runs, whether the caller's grant covers the request.
1. Start the Go service and its sidecar
You need Go 1.21 or newer. The launcher fetches chio-go-http, builds the service, starts it on port 8101 beside a sidecar on 9101, and issues a five-minute grant to Basalt's buying agent. You can also download the project.
chio new pricing-go-cpp --example pricing-go-cpp && cd pricing-go-cpp
python3 run.py2. Quote and list
python3 run.py quote sku-204 25
python3 run.py list{
"status": 201,
"body": {
"currency": "USD", "id": 1, "percent": 25, "price": "43.50",
"receipt_id": "4b1e…9c07", "sku": "sku-204"
},
"headers": { "x-chio-receipt-id": "4b1e…9c07" }
}
{
"status": 200,
"body": { "quotes": [{ "id": 1, "sku": "sku-204", "percent": 25, "price": "43.50" }] },
"headers": {}
}You should see a 201 with USD 43.50 for sku-204 at 25 percent and the admission receipt's ID, then the one recorded quote. python3 run.py revoke withdraws the grant; the same quote then returns 403 and records nothing.
Go: wrap the HTTP handler
chio.Protect takes an ordinary http.Handler, so the chi router stays inside it. The service answers /healthz before the wrapper and caps the body before the adapter buffers it.
protected := chio.Protect(router, chio.WithSidecarURL(sidecar))
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/healthz" {
send(w, http.StatusOK, map[string]string{"status": "ready", "framework": "go"})
return
}
// Cap bytes before the adapter collects and hashes the body.
r.Body = http.MaxBytesReader(w, r.Body, 8192)
protected.ServeHTTP(w, r)
})Depend on the published adapter
Read go.mod · complete 8 lines · complete file
module pricing-go
go 1.21
require (
github.com/backbay-labs/chio/sdks/go/chio-go-http v0.2.0
github.com/go-chi/chi/v5 v5.2.3
)The complete Go service
Read main.go · complete 81 lines · complete file
package main
import (
"encoding/json"
"errors"
"io"
"log"
"net/http"
"os"
"time"
chio "github.com/backbay-labs/chio/sdks/go/chio-go-http"
"github.com/go-chi/chi/v5"
)
func send(w http.ResponseWriter, status int, value any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(value)
}
func main() {
store, err := openStore(".pricing/go-quotes.json")
if err != nil {
log.Fatal(err)
}
router := chi.NewRouter()
router.Get("/v1/quotes", func(w http.ResponseWriter, r *http.Request) {
send(w, http.StatusOK, map[string]any{"quotes": store.list()})
})
router.Post("/v1/quote", func(w http.ResponseWriter, r *http.Request) {
var payload struct {
SKU *string `json:"sku"`
Percent *float64 `json:"percent"`
}
decoder := json.NewDecoder(r.Body)
decoder.DisallowUnknownFields()
if err := decoder.Decode(&payload); err != nil || payload.SKU == nil || payload.Percent == nil {
send(w, http.StatusBadRequest, map[string]string{"error": "body must contain exactly sku and percent"})
return
}
if err := decoder.Decode(new(any)); err != io.EOF {
send(w, http.StatusBadRequest, map[string]string{"error": "expected one JSON object"})
return
}
quote, err := store.issue(*payload.SKU, *payload.Percent)
if err != nil {
status := http.StatusInternalServerError
if errors.Is(err, errInvalidQuote) {
status = http.StatusBadRequest
}
send(w, status, map[string]string{"error": err.Error()})
return
}
send(w, http.StatusCreated, map[string]any{
"id": quote.ID, "sku": quote.SKU, "percent": quote.Percent,
"price": quote.Price, "currency": "USD",
"receipt_id": w.Header().Get("X-Chio-Receipt-Id"),
})
})
sidecar := os.Getenv("CHIO_SIDECAR_URL")
if sidecar == "" {
sidecar = "http://127.0.0.1:9101"
}
protected := chio.Protect(router, chio.WithSidecarURL(sidecar))
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/healthz" {
send(w, http.StatusOK, map[string]string{"status": "ready", "framework": "go"})
return
}
// Cap bytes before the adapter collects and hashes the body.
r.Body = http.MaxBytesReader(w, r.Body, 8192)
protected.ServeHTTP(w, r)
})
server := &http.Server{
Addr: "127.0.0.1:8101", Handler: handler,
ReadHeaderTimeout: 5 * time.Second,
}
log.Print("Go pricing: http://127.0.0.1:8101")
log.Fatal(server.ListenAndServe())
}Read the price table and quote store 92 lines · complete file
package main
import (
"encoding/json"
"errors"
"fmt"
"math"
"os"
"path/filepath"
"sync"
)
var errInvalidQuote = errors.New("invalid quote request")
// Larkfield list prices in cents, as main holds them after the Quickstart.
var prices = map[string]int64{"sku-118": 2400, "sku-204": 5800, "sku-301": 14000}
type Quote struct {
ID int `json:"id"`
SKU string `json:"sku"`
Percent float64 `json:"percent"`
Price string `json:"price"`
}
type QuoteStore struct {
sync.Mutex
path string
quotes []Quote
}
func openStore(path string) (*QuoteStore, error) {
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
return nil, err
}
store := &QuoteStore{path: path, quotes: []Quote{}}
data, err := os.ReadFile(path)
if errors.Is(err, os.ErrNotExist) {
return store, nil
}
if err != nil {
return nil, err
}
return store, json.Unmarshal(data, &store.quotes)
}
func (s *QuoteStore) list() []Quote {
s.Lock()
defer s.Unlock()
return append([]Quote{}, s.quotes...)
}
func (s *QuoteStore) issue(sku string, percent float64) (Quote, error) {
cents, ok := prices[sku]
if !ok {
return Quote{}, fmt.Errorf("%w: unknown sku %s", errInvalidQuote, sku)
}
if percent < 0 || percent > 100 {
return Quote{}, fmt.Errorf("%w: percent must be between 0 and 100", errInvalidQuote)
}
// Basis points keep the arithmetic exact; the last step rounds half up to the cent.
basis := int64(math.Round(percent * 100))
price := (cents*(10000-basis) + 5000) / 10000
s.Lock()
defer s.Unlock()
quote := Quote{ID: len(s.quotes) + 1, SKU: sku, Percent: percent, Price: fmt.Sprintf("%d.%02d", price/100, price%100)}
next := append(append([]Quote{}, s.quotes...), quote)
data, err := json.MarshalIndent(next, "", " ")
if err != nil {
return Quote{}, err
}
file, err := os.CreateTemp(filepath.Dir(s.path), "quotes-*.tmp")
if err != nil {
return Quote{}, err
}
defer os.Remove(file.Name())
if _, err = file.Write(data); err != nil {
file.Close()
return Quote{}, err
}
if err = file.Sync(); err != nil {
file.Close()
return Quote{}, err
}
if err = file.Close(); err != nil {
return Quote{}, err
}
if err = os.Rename(file.Name(), s.path); err != nil {
return Quote{}, err
}
s.quotes = next
return quote, nil
}The handler reads the admitted receipt ID from the X-Chio-Receipt-Id header on the response writer; the Go adapter adds nothing to the request context.
C++: govern named Drogon routes
The Drogon service serves the same contract on port 8102. Each governed route names ChioMiddleware in its registration; /healthz does not. Stop the Go service, then:
python3 run.py --framework drogonThe setup script builds JsonCpp and Drogon into .deps/prefix, and CMake fetches the Drogon adapter and its Rust FFI from the released v0.2.0 tag. You need CMake 3.24, a C++17 compiler, Rust, and the OpenSSL, libcurl, UUID, and zlib development libraries; Drogon's installation guide lists them per platform.
chio::drogon::Options options;
const auto sidecar = std::getenv("CHIO_SIDECAR_URL");
options.sidecar_url = sidecar ? sidecar : "http://127.0.0.1:9101";
chio::drogon::configure(std::move(options));Require the adapter
Read CMakeLists.txt · complete 16 lines · complete file
cmake_minimum_required(VERSION 3.24)
project(LarkfieldPricingNative LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CHIO_DROGON_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(CHIO_DROGON_REQUIRE_DEPS ON CACHE BOOL "" FORCE)
include(FetchContent)
# The Drogon adapter and its Rust FFI come from the released Chio tag.
FetchContent_Declare(chio_drogon
GIT_REPOSITORY https://github.com/backbay-labs/chio.git
GIT_TAG v0.2.0
GIT_SHALLOW TRUE
SOURCE_SUBDIR sdks/cpp/chio-drogon)
FetchContent_MakeAvailable(chio_drogon)
add_executable(pricing-drogon cpp/main.cpp)
target_link_libraries(pricing-drogon PRIVATE ChioDrogon::chio_drogon)The build links ChioDrogon::chio_drogon and fails when Drogon is missing, so a service never starts without its admission check.
The complete C++ service
Read cpp/main.cpp · complete 131 lines · complete file
#include <drogon/drogon.h>
#include <chio/drogon.hpp>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <map>
#include <mutex>
#include <stdexcept>
namespace fs = std::filesystem;
// Larkfield list prices in cents, as main holds them after the Quickstart.
const std::map<std::string, long long> prices{{"sku-118", 2400}, {"sku-204", 5800}, {"sku-301", 14000}};
class QuoteStore {
std::mutex gate;
fs::path path = ".pricing/drogon-quotes.json";
Json::Value quotes{Json::arrayValue};
public:
QuoteStore() {
if (fs::create_directories(path.parent_path())) {
fs::permissions(path.parent_path(), fs::perms::owner_all);
}
if (fs::exists(path)) {
std::ifstream input(path);
Json::CharReaderBuilder reader;
std::string errors;
if (!Json::parseFromStream(reader, input, "es, &errors) || !quotes.isArray()) {
throw std::runtime_error("Cannot read issued quotes: " + errors);
}
}
}
Json::Value list() {
std::lock_guard<std::mutex> lock(gate);
return quotes;
}
Json::Value issue(const std::string& sku, double percent) {
// Basis points keep the arithmetic exact; the last step rounds half up to the cent.
const auto basis = static_cast<long long>(std::llround(percent * 100));
const auto cents = (prices.at(sku) * (10000 - basis) + 5000) / 10000;
char price[32];
std::snprintf(price, sizeof price, "%lld.%02lld", cents / 100, cents % 100);
std::lock_guard<std::mutex> lock(gate);
Json::Value quote;
quote["id"] = quotes.size() + 1;
quote["sku"] = sku;
quote["percent"] = percent;
quote["price"] = price;
quote["currency"] = "USD";
auto next = quotes;
next.append(quote);
const auto temporary = path.string() + ".tmp";
try {
std::ofstream output(temporary, std::ios::trunc | std::ios::binary);
output.exceptions(std::ios::failbit | std::ios::badbit);
Json::StreamWriterBuilder writer;
output << Json::writeString(writer, next);
output.close();
fs::rename(temporary, path);
} catch (...) {
fs::remove(temporary);
throw;
}
quotes = next;
return quote;
}
};
using Reply = std::function<void(const drogon::HttpResponsePtr&)>;
void reply(Reply& callback, Json::Value body, drogon::HttpStatusCode status) {
auto response = drogon::HttpResponse::newHttpJsonResponse(body);
response->setStatusCode(status);
callback(response);
}
void error(Reply& callback, const std::string& message, drogon::HttpStatusCode status) {
Json::Value body;
body["error"] = message;
reply(callback, body, status);
}
int main() {
auto store = std::make_shared<QuoteStore>();
chio::drogon::Options options;
const auto sidecar = std::getenv("CHIO_SIDECAR_URL");
options.sidecar_url = sidecar ? sidecar : "http://127.0.0.1:9101";
chio::drogon::configure(std::move(options));
drogon::app().registerHandler("/healthz",
[](const drogon::HttpRequestPtr&, Reply&& callback) {
Json::Value body;
body["status"] = "ready";
body["framework"] = "drogon";
reply(callback, body, drogon::k200OK);
}, {drogon::Get});
drogon::app().registerHandler("/v1/quotes",
[store](const drogon::HttpRequestPtr&, Reply&& callback) {
Json::Value body;
body["quotes"] = store->list();
reply(callback, body, drogon::k200OK);
}, {drogon::Get, "chio::drogon::ChioMiddleware"});
drogon::app().registerHandler("/v1/quote",
[store](const drogon::HttpRequestPtr& request, Reply&& callback) {
const auto payload = request->getJsonObject();
if (!payload || !payload->isObject() || payload->size() != 2
|| !(*payload)["sku"].isString() || !(*payload)["percent"].isNumeric()) {
error(callback, "body must contain exactly sku and percent", drogon::k400BadRequest);
return;
}
const auto sku = (*payload)["sku"].asString();
const auto percent = (*payload)["percent"].asDouble();
if (prices.find(sku) == prices.end()) {
error(callback, "unknown sku " + sku, drogon::k400BadRequest);
return;
}
if (percent < 0 || percent > 100) {
error(callback, "percent must be between 0 and 100", drogon::k400BadRequest);
return;
}
try {
auto quote = store->issue(sku, percent);
quote["receipt_id"] = chio::drogon::receipt_id(request);
reply(callback, quote, drogon::k201Created);
} catch (const std::exception& failure) {
error(callback, failure.what(), drogon::k500InternalServerError);
}
}, {drogon::Post, "chio::drogon::ChioMiddleware"});
drogon::app().setClientMaxBodySize(8192).addListener("127.0.0.1", 8102).run();
}chio::drogon::receipt_id(request) gives the handler its receipt ID.
Make the governed routes explicit
| Concern | Go | Drogon |
|---|---|---|
Address | 127.0.0.1:8101 | 127.0.0.1:8102 |
Governed routes | Everything inside the wrapper; /healthz answered first | Routes that name ChioMiddleware |
Quotes on disk | .pricing/ | .pricing/ |
Receipt in the handler | X- on the response writer | chio:: |
Admission and the quote write are separate steps; the example does not claim one transaction across them.
Read your own receipts
python3 receipts.py --decision deny71c0…e2d4 DENY POST /v1/quote HTTP 403
receipts.py opens .pricing/receipts.db read-only; pass a receipt ID in place of --decision to print that complete signed record, and post it to /chio/verify as HTTP receipt verification shows. A request for sku-999 with a valid grant is admitted and then rejected by the handler with 400; its receipt records the decision, not the handler's answer:
{
"verdict": { "verdict": "allow" },
"response_status": 200,
"metadata": { "chio_http_status_scope": "decision" }
}A refused quote never reaches the handler
{
"error": "chio_access_denied",
"message": "side-effect route requires a capability token",
"receipt_id": "71c0…e2d4"
}Read each failure at its boundary
| Situation | HTTP result | Meaning |
|---|---|---|
No grant | 403 | Chio refused admission; nothing is recorded. |
Grant, known SKU | 201 | The handler issued and recorded the quote. |
Grant, sku-999 | 400 | Admission allowed; the handler rejected the SKU. |
Body over 8192 bytes | Go 400, Drogon 413 | Stopped before admission; no receipt. |
Sidecar down | 502; /healthz stays 200 | No decision was possible. Both adapters fail closed. |
Check the whole contract in one run
With the launcher stopped, python3 run.py check refuses a quote with no grant, issues one with Basalt's grant, rejects sku-999 after admission, revokes the grant, refuses again, and confirms the refused requests recorded nothing. Add --framework drogon for the C++ service.
Require a grant to read quotes
Issued quotes are commercial terms Basalt should not browse freely. Mark the read operation side-effecting in openapi.yaml, restart the launcher with the same .pricing state, and read with and without the grant.
paths:
/v1/quotes:
get:
operationId: listQuotes
x-chio-side-effects: true
responses:
"200": { description: Up to 100 issued quotes }
# Keep the existing /v1/quote post operation here.curl -sS -w ' HTTP %{http_code}\n' http://127.0.0.1:8101/v1/quotes
curl -sS http://127.0.0.1:8101/v1/quotes \
-H "X-Chio-Capability: $(jq -c . .pricing/capability.json)"{"error":"chio_access_denied","message":"side-effect route requires a capability token","receipt_id":"a90d…11f3"} HTTP 403
{"quotes":[{"id":1,"sku":"sku-204","percent":25,"price":"43.50"}]}The read without a grant is refused, and the read with Basalt's grant returns the same quote. This changes the matched operation only: the OpenAPI document is not an exhaustive allowlist, and unmatched safe methods keep the method-based default.