Library / SDK
HashLoad/horse avatar
HashLoad/horse

Horse picks its transport at compile time, so Delphi and FPC defaults differ

Project brief: Fast, opinionated, minimalist web framework for Delphi. Horse is an Express -inspired web framework for Delphi and Lazarus.

1,377 stars250 forksPascalMIT

At a glance

What is it?
HashLoad/horse is an Express-inspired Pascal web framework whose handler code stays identical while the socket layer underneath changes per compiler. The install is one boss command, and the doc/ wiki is unusually specific about shutdown telemetry and failure testing.
Who is it for?
Pick Horse when the team already writes Pascal and wants an Express shape without giving up a native compiler or a kernel-mode HTTP stack on Windows. Verify first that the transport you need has a Lazarus column filled in if you build under FPC, since ICS is Delphi only, and that your IDE can load the doc/ skills through .agents/skills.json.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Pascal, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The default transport is decided by the compiler, not the project

A provider is the HTTP transport that owns the socket and hands requests to your route handlers, and Horse chooses one at compile time rather than at runtime. The default is not one library for everyone. On Delphi, self-hosted builds fall back to Indy, which is aimed at Console, VCL and Daemon executables. On Free Pascal the default is fphttpserver, which is aimed at Daemon, HTTPApplication and LCL. Neither default needs a conditional define, so a project that compiles under both compilers ends up with a working socket layer in each without anyone editing the handler code. That is the whole point of the split: the handler signature does not move when the transport does. A route declared with THorse.Get keeps its arguments under every provider because the provider only changes how bytes arrive. Anything that replaces the blocking default, such as CrossSocket or mORMot2, is opt-in and costs a define at the top of the project.

HttpSys is the only transport listed as built into Horse

The provider table does not treat its rows evenly. horse-provider-crosssocket and horse-provider-mormot are marked as new, work under both Delphi and Lazarus, and are selected with HORSE_CROSSSOCKET and HORSE_PROVIDER_MORMOT. Both swap the blocking defaults for async IOCP, epoll or kqueue I/O. The ICS row carries a cross in the Lazarus column: HORSE_PROVIDER_ICS is Delphi only, targets Windows plus Linux64 and macOS, and substitutes OverbyteICS with OpenSSL 3.x or 4.x, TLS 1.3, SNI and mTLS. HTTP.sys is the exception to the external-package pattern. HORSE_PROVIDER_HTTPSYS drives the kernel-mode http.sys stack that IIS itself runs on, and the table marks it as built into Horse with no external library to install. HORSE_PROVIDER_EPOLL points at the Linux native asynchronous event loop. Transports with no row in that table, among them gRPC, Apache, ISAPI, CGI and daemons, are handled on their own in doc/providers.md.

Two quickstarts, one Listen call, different code shapes

The Delphi sample registers an anonymous procedure inline, which needs no forward declaration and no mode directive:

delphi
uses Horse;

begin
  THorse.Get('/ping',
    procedure(Req: THorseRequest; Res: THorseResponse)
    begin
      Res.Send('pong');
    end);

  THorse.Listen(9000);
end.

The Lazarus sample opens with {$MODE DELPHI}{$H+}, lifts the same handler into a named procedure called GetPing, and then registers it by name, THorse.Get('/ping', GetPing). Both end on the identical THorse.Listen(9000) with no configuration object, no server variable and no explicit bind address anywhere in view. Port 9000 is hardcoded in both samples rather than read from a file or an environment value, so a real deployment has to change that argument. The lowercase pong in the Delphi body and the capitalised Pong in the Lazarus body are the only substantive difference between the two samples beyond the handler shape, which is a fair measure of how small the framework surface is at the entry point.

AI instruction files live in doc/skills and load through .agents/skills.json

The repository ships pre-packaged instruction files under doc/skills so an agent writes Horse code instead of guessing at it. Antigravity, GitHub Copilot and Claude are all named as targets, and the stated goal is idiomatic, thread-safe and memory-safe Horse code. Antigravity is the one path that loads automatically: add the doc directory of a local Horse checkout to a local .agents/skills.json file and every skill in it is picked up. The file is a single entries array holding one path object per skill source:

json
{
  "entries": [
    { "path": "path/to/horse/doc" }
  ]
}

For Copilot, Claude and custom agents the path is manual, since the instructions say to refer to the files in doc/skills to feed context to the agent. There is a third option: copy the skill folders straight into the project .agents/skills directory. The .agents entry also appears in the top-level tree, so the mechanism is part of the repository layout rather than a user convention.

boss install horse, and a lockfile that pins the result

Installation is a single command issued through HashLoad's own package manager, not a manual download of a zip:

sh
boss install horse

An optional second piece is the Horse Wizard, a separate repository that adds IDE integration, so a headless build can skip it without losing anything from the framework itself. Two package manifests sit at the top level next to the source: boss.json, which declares what the package needs, and boss-lock.json, which is the resolved dependency set. The release checklist in doc refers to test dependency lockfiles, so the same pinning idea is applied to the test side. Localisation is real rather than decorative: the tree carries README.pt-BR.md, CONTRIBUTING.pt-BR.md, CONTRIBUTING.md and README.md, so Portuguese readers get the install path and the contribution rules in their own language. Alongside them sit samples/delphi, samples/lazarus, samples/grpc and samples/tests, plus tests and tools directories, which is what makes the two quickstarts runnable side by side instead of being snippets detached from a working tree.

The wiki spends pages on shutdown telemetry and integrity testing

doc/index.md is a table of nineteen topics, and two of them deal with what happens when things go wrong rather than with routing. Graceful Shutdown is described as draining active connections, and it names two pieces of telemetry for the process: an ActiveRequests counter and an IsShuttingDown flag, both intended for exposure rather than for internal bookkeeping. Integrity Testing covers automated integration tests, resilience against Access Violation, and SO limit testing. Those two pages describe a framework that expects to be interrogated, and doc/telemetry.md adds OpenTelemetry distributed tracing with Prometheus metrics collection on top. Elsewhere the table commits to native bi-directional WebSockets under RFC 6455, native streaming through Web Streams and SSE, a thread-safe memory buffer pool built to eliminate heap allocation, and Multi-Instance support for running isolated HTTP servers inside one process. doc/deployment.md is a one-page recipe across Console, VCL, Daemon, Windows Service, LCL and HTTPApplication, and doc/writing-middleware.md covers provider neutrality and Boss packaging for third-party middleware.

Three patch releases landed inside nine days

The recent release list is dense. Version 3.3.8 was published on 2026-09-16, 3.3.9 on 2026-09-17, and 3.3.10 on 2026-09-26, so two of the three shipped on consecutive days and the last push to master came on 2026-09-30. Nothing in the source marks the repository as archived, and the release checklist at doc/release-checklist.md covers versioning, release validation and tags. The licence is MIT and the primary language is Pascal, with master as the default branch and no project homepage declared. A contributor guide exists in English and Portuguese, which pairs with the README pair. The practical reading is that maintenance is frequent rather than staged, so anyone adopting Horse should expect patch numbers to move quickly and should track the tag list rather than a single pinned commit.

Editorial conclusion

Pick Horse when the team already writes Pascal and wants an Express shape without giving up a native compiler or a kernel-mode HTTP stack on Windows. Verify first that the transport you need has a Lazarus column filled in if you build under FPC, since ICS is Delphi only, and that your IDE can load the doc/ skills through .agents/skills.json. Ignore it if you need a runtime-swappable transport or a framework whose release history is spaced by months rather than days.

Frequently asked questions

How do you install the Horse web framework?

Through the boss package manager with boss install horse. The optional Horse Wizard adds IDE integration and is a separate repository you can skip on a headless build.

Which transport provider does Horse use by default?

Indy on Delphi for Console, VCL and Daemon executables, and fphttpserver on FPC for Daemon, HTTPApplication and LCL. Neither default requires a conditional define.

Does Horse support WebSockets and streaming responses?

Yes. doc/websocket.md covers native bi-directional WebSocket connections under RFC 6455, and doc/streaming.md covers native streaming through Web Streams and server-sent events.

Can a Horse process serve more than one HTTP server?

The Multi-Instance page documents running and isolating multiple independent HTTP servers concurrently inside the same process.

How does Horse handle graceful shutdown?

doc/graceful-shutdown.md describes draining active connections and names two telemetry values for the process, an ActiveRequests counter and an IsShuttingDown flag.

How do I get an AI agent to write Horse code?

Instruction files ship in doc/skills. Antigravity loads them by adding the Horse doc directory to a local .agents/skills.json file, while Copilot and Claude get the same files referred to manually or copied into the project .agents/skills directory.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hashload-horse.svg)](https://hysenlabs.com/projects/hashload-horse)