From Fork to Framework, Part 1: Standing on Shoulders
We started AOCodex and AOSentry by forking two excellent open-source projects. Here is what we learned about the limits of that approach.

The Fork-Then-Diverge Pattern
Every engineering team that has launched a product on top of an open-source fork knows the feeling: the first two weeks are intoxicating. You inherit years of upstream work, a working CI pipeline, a contributor community that has already filed the bugs you would have discovered the hard way. You feel like you have cheated the calendar.
Then the merge conflicts arrive.
This post is an honest account of what happened when we built the first generation of AO Cyber Systems on two open-source forks – and why we eventually abandoned both. If you are evaluating a fork-based strategy for your own product, we hope the timeline, the architecture, and the lessons save you some pain.
The Two Forks
On October 2, 2025, we created two repositories:
AOCodex, forked from LibreChat by Danny Avila. LibreChat is a TypeScript/React/Node.js chat application with over 3,200 upstream commits at the time of our fork. It gave us a polished multi-model chat UI, conversation management, plugin architecture, and a MongoDB persistence layer – essentially, a production-ready ChatGPT alternative we could rebrand and extend.
AOSentry, forked from LiteLLM by BerriAI. LiteLLM is a Python proxy that presents a unified OpenAI-compatible API across dozens of model providers. With over 26,000 upstream commits, it is one of the most actively developed projects in the LLM infrastructure space. It gave us provider abstraction, token tracking, rate limiting, and a Next.js admin dashboard.
Together, these two forks formed the backbone of our v1 architecture.
The V1 Architecture
The architecture split responsibilities cleanly in theory: AOCodex owned the user experience, conversation state, and authentication; AOSentry owned model routing, cost tracking, and provider failover. In practice, the boundary was messier than it looked on a whiteboard. Both projects carried their own admin UIs, their own auth systems, and their own opinions about how to talk to a database.
The Timeline
What follows is the condensed story of 10 weeks on AOCodex and roughly 19 weeks on AOSentry. The branch names in our git history tell it more honestly than any retrospective could.
Week 1-2 (October 2-15, 2025): The Honeymoon
Everything moved fast. We rebranded both projects, swapped color schemes, configured CI, and deployed to staging. AOCodex inherited LibreChat’s Docker Compose setup, so we had a working multi-container environment on day one. AOSentry’s proxy layer worked immediately with our existing OpenAI and Anthropic API keys.
The first custom features landed: Stripe billing integration on AOCodex, PII detection using Microsoft Presidio on AOSentry, and a shared guardrails pipeline that could intercept prompts before they reached a model provider.
Week 3-5 (October 16 - November 5, 2025): Divergence Begins
We were committing heavily – AOCodex would accumulate 522 custom commits by December 12. Features included admin dashboards, Docker Swarm deployment manifests, and multi-architecture container builds for ARM and x86. Each feature pulled us further from upstream.
The Recoil-to-Jotai migration was our first taste of upstream turbulence. LibreChat switched its state management library mid-stream, and because we had modified dozens of components that imported Recoil atoms, the migration touched files across our entire fork. It was a three-day detour that delivered zero user-facing value.
Week 6-7 (November 6-19, 2025): The 759-File Merge
The branch feature/AOC-61-merge-latest-librechat-code tells the story of our attempt to pull in LibreChat v0.8.1-rc1. The merge produced 759 file conflicts. Not 759 lines – 759 files.
Some of these conflicts were trivial: both sides had touched package.json or configuration files. Many were not. Our Stripe billing hooks had been wired into LibreChat’s user model, which upstream had restructured. Our guardrails middleware sat in a request pipeline that upstream had refactored. Our admin dashboard components imported utilities that upstream had moved to different modules.
We created backup-pre-librechat-merge-20251112 before attempting the merge – a branch name that radiates confidence. The merge took five full engineering days to resolve, test, and stabilize. During that time, no product features shipped.
Week 8-10 (November 20 - December 12, 2025): The LangChain Saga
On AOSentry’s side, the LangChain v1 upgrade was the equivalent of the 759-file merge. LangChain’s migration from v0 to v1 changed import paths, renamed core abstractions, and deprecated patterns we had built on top of. Because AOSentry inherited LiteLLM’s LangChain integration, and we had extended it with custom chain logic for our guardrails pipeline, the upgrade cascaded through a significant portion of our codebase.
Meanwhile, the refactor/remove-litellm-branding branch represented our effort to white-label AOSentry. Rebranding a project with 26,000 commits is not a find-and-replace operation. The name appeared in log messages, error strings, API response headers, database migration comments, documentation, Docker image tags, environment variable prefixes, and Prometheus metric names. Every occurrence had to be evaluated: some were internal, some were user-facing, and some were contractual (LiteLLM’s enterprise license, which we needed to remove entirely and replace with our own licensing logic).
December 12, 2025: The Decision
By mid-December, we had 522 custom commits on AOCodex and 188 on AOSentry. We had built real product value: a working Stripe billing system, PII detection, content guardrails, admin dashboards, Docker Swarm orchestration, and multi-arch builds. But we had also spent a disproportionate amount of engineering time on merge conflicts, upstream migrations, and rebranding work that did not move the product forward.
The fork-based approach had another cost that is harder to quantify: architectural ceiling. Both upstream projects made reasonable choices for their use cases that were wrong for ours. LibreChat used MongoDB because document storage is natural for chat conversations – but we needed relational integrity for billing, teams, and access control. LiteLLM used Prisma with PostgreSQL, which was closer to what we wanted, but its schema was optimized for a proxy’s concerns (API keys, spend tracking, rate limits) rather than a product’s concerns (users, organizations, subscriptions, audit logs).
We could not change the database layer of either fork without essentially rewriting them. And if we were going to rewrite them, the fork was no longer saving us time.
What We Built (That Mattered)
It is important to acknowledge what the fork period produced beyond frustration. Several capabilities we prototyped during these ten weeks survived into later iterations:
| Capability | Origin | Survived? |
|---|---|---|
| Stripe billing integration | AOCodex custom | Yes – billing logic carried forward |
| PII detection (Presidio) | AOSentry custom | Yes – became a core guardrail |
| Content guardrails pipeline | Both forks | Yes – architecture refined in v2 |
| Docker Swarm deployment | AOCodex custom | No – replaced by App Platform, then Kubernetes |
| Multi-arch container builds | AOCodex custom | Yes – still building ARM + x86 |
| Admin dashboards | Both forks | Partially – rebuilt in each subsequent iteration |
| LiteLLM provider abstraction | AOSentry upstream | Yes – used as a library in v2, not a fork |
The fork period was not wasted. It was a rapid, high-fidelity prototyping phase that taught us what our product needed to be. The mistake would have been staying in that phase longer than necessary.
The Lessons
Forks are prototypes, not foundations
A fork gives you a running start on features, but it also gives you a running start on technical debt. Every upstream merge is a tax. Every upstream architectural decision you disagree with is a constraint. The more you customize, the higher the tax and the tighter the constraints.
Upstream velocity is a double-edged sword
We chose LibreChat and LiteLLM partly because they were active projects with strong communities. That activity meant bug fixes and new features we could inherit – but it also meant the codebase was a moving target. A dormant upstream is easier to fork; an active upstream is more valuable to fork but harder to maintain.
Rebranding is architectural work
Removing another project’s identity from a large codebase is not cosmetic. It touches error handling, logging, metrics, documentation, environment variables, API contracts, and sometimes licensing. Budget for it accordingly, or accept that your product will carry another project’s name in places your customers might see.
The merge conflict is the symptom, not the disease
The 759-file merge was painful, but the real problem was that we had modified 522 files in a codebase we did not control. Every custom commit increased the surface area for future conflicts. There is no tooling that solves this; it is a fundamental tension in the fork model.
Credit Where It Is Due
Danny Avila’s LibreChat and BerriAI’s LiteLLM are excellent projects. LibreChat gave us a mature, well-tested chat interface that would have taken months to build from scratch. LiteLLM’s provider abstraction layer is genuinely best-in-class – we still use it today, as a dependency rather than a fork. Both projects have active communities, responsive maintainers, and clear roadmaps.
Our decision to move away from the forks was not a commentary on their quality. It was a recognition that our product requirements had diverged far enough from their goals that maintaining alignment was costing more than building independently.
What Came Next
The fork experiment ended in mid-December 2025. What replaced it was a clean-room rewrite using FastAPI and SvelteKit – a story with its own victories and its own hard lessons.
That is Part 2.