Obliga · technical summary
A single-tenant .NET 10 / Blazor Server / Azure SQL application. One instance and one database per customer, with isolation by deployment rather than by a tenant column. This page is written for people who will evaluate that, including the parts that count against it.
Obliga is a focused product, not a platform. It keeps a governed record of the contracts you've signed and makes sure a renewal or notice deadline never passes unnoticed. It doesn't do drafting, e-signature, or negotiation. It only makes sense for organizations already on Microsoft 365, because it runs on what you already have: staff sign in with their existing accounts, the people list comes from your directory, and reminders are sent from your own mailbox. Those reminders only ever go to your own staff. Obliga never emails a counterparty.
Stack
| App | .NET 10, Blazor Server, MudBlazor |
|---|---|
| Data | EF Core (Microsoft's object-to-database mapper) over Azure SQL. Every database operation opens its own short-lived connection through IDbContextFactory, rather than sharing one long-lived DbContext handed out by dependency injection. In Blazor Server a user's session stays open for hours, so a shared one would be held for that entire time and used by overlapping operations, which EF Core explicitly doesn't support. |
| Identity | Entra ID (OIDC) for sign-in; Microsoft Graph for directory sync and mail |
| Secrets | Managed identity wherever Azure allows it. The interactive sign-in registration is the one place a client secret is genuinely required; it lives in Key Vault, never in configuration. |
| Infrastructure | Bicep, Azure's infrastructure-as-code language, applied with the az command-line tool. The whole environment (app, database, storage, key vault, permissions) is described in version-controlled files and created by running a script, rather than assembled by hand in the Azure portal. The same templates deploy onto infrastructure we run, or into your own Azure subscription. |
| Hosting | One App Service and one Azure SQL database per customer. Databases share a logical server; apps share an App Service Plan. |
Layout
Each layer depends only on the one below it, and Domain depends on nothing at all.
That matters for one reason. The reminder-scheduling engine lives there: working out which dates a
notice fires on, counting business days, handling a decision that pauses the schedule versus one
that ends it, and the failsafe window that catches a deadline nobody acted on. It's the part of
Obliga where a subtle bug doesn't throw an error; it just quietly fails to remind someone. Because
it's plain C# with no database or network involved, thousands of date scenarios can be tested in
seconds, which is why most of the test suite points at it.
Guarantees
These four are enforced by the structure of the code, not by a developer remembering:
Beyond those, the codebase holds itself to conventions that are genuinely conventions: configuration over hardcoded business constants, UTC timestamps with date maths isolated in one service, consistent keys and naming. They are documented and reviewed, but honesty requires saying that they are enforced by discipline rather than by the compiler.
Trade-offs
UI state lives on the server and each user holds an open WebSocket. That buys a small, simple codebase with no separate API surface, and it costs you offline tolerance. A flaky connection means a visible reconnect. It suits staff working from an office or home broadband, and suits a field workforce on patchy mobile data far less.
Three background jobs run inside the application itself: sending reminders, sending the weekly digest (a summary email giving each contract manager their whole portfolio at a glance), and syncing staff from your directory. Nothing coordinates which copy of the app should run them, so each customer runs a single copy. The upside is strong isolation and simple reasoning about what is happening. Azure's 99.95% availability commitment applies to that single instance, so expected uptime is high; the practical cost is that a deployment or a platform restart is a brief interruption rather than a seamless one, which is why updates happen in an agreed window.
Your staff list is read from Microsoft Graph, and reminders are sent from a shared mailbox in your own tenant. There is no other way to get people into the system and no SMTP alternative, so Obliga cannot run without it.
That same dependency is what keeps your data safer. Obliga never stores a password, so it holds no credential of yours to lose. Sign-in happens in your tenant, so the multi-factor, conditional access, and device rules your IT already enforces apply here with nothing to configure. Disable an account on someone's last day and they lose access to Obliga at that moment, with no second account to remember. Reminders leave from your own mailbox, so they stay inside your retention and DLP rules rather than being sent on your behalf by a third party.
Setting up a new environment is done with Bicep, Azure's infrastructure-as-code language, so the servers and databases are described in version-controlled files rather than clicked together by hand. Releasing new application code is a packaged build pushed to Azure. Both are repeatable and reviewable, but neither is yet a CI/CD pipeline: the automated chain that builds, tests and releases every approved change on its own, with checks that block a bad release. Today a person runs the release step. That is on the roadmap.
Who builds it
Obliga is designed and directed by Ian Johns, and built with Claude Code (Fable, Opus 5, Sonnet 5). Ian is an application developer with decades of experience in software design, consulting, and technical architecture, specialising in simplifying complex systems and delivering scalable, maintainable solutions. The background is squarely Microsoft: Power Platform, SharePoint, Dataverse, Teams, .NET, and SQL Server, across both cloud and on-premises architecture, and across the full project lifecycle from design through delivery and support.
That matters here for one reason in particular: Obliga is a small product with a narrow job, and the risk with small products is not that they lack features, it is that nobody thought carefully about the data model, the failure modes, or what happens on the day something goes wrong. Those are the parts that got the attention.
It is also worth being direct about what that division of labour means, because it is a fair question to ask in 2026. The architecture, the standing rules, and the design brief are defined and maintained by hand. Those rules live in the repository and constrain what gets written; every change is reviewed, built, and tested before it ships. The result is that a solo developer can move at a pace that would normally need a team, without the codebase turning into something nobody understands. The judgement about what to build, what to refuse, and what counts as correct remains a human one.
Exit
Your data is yours, and it comes out without anyone's permission. Everything below is a button in the product rather than a support request, so there is no ticket to raise and nobody to wait for.
Some of it. Any view of the contract grid exports to CSV or Excel, matching whatever filter, search, or saved view is on screen. That is the everyday case: give the auditor the twelve agreements they asked about, or hand a department its own list. The same view also exports as a zip that includes the documents, so what you send is the records and their files together rather than a spreadsheet and a promise to follow up.
Or all of it. One button on the Admin screen takes the complete record: every contract whatever its status, active and closed and cancelled together, with every attached document, as a single archive. A CSV of the metadata, then one folder per contract holding its files. Moving a portfolio of hundreds of documents to another system is one download rather than hundreds, and there is nothing to ask us for. Separately, an administrator can export the full field-level audit trail for a compliance record.
Each archive carries a short readme describing exactly what is in it and how it was produced, because "is this everything?" is the first question anyone asks of an export they have been handed.
The other fair question, given that Obliga is built by one person, is what happens if that person is not available. Source code escrow can be arranged on request: an independent agent holds a copy of the source and releases it to you under conditions agreed in advance, such as the business ceasing to trade or failing to meet its support obligations. Very few customers ever need to call on an escrow agreement. It should still be there for the asking rather than something you have to think to negotiate for.
361 automated tests, weighted heavily toward the scheduling engine's business-day and cadence maths, since that is the one place a subtle bug silently costs a customer a missed renewal rather than showing an error.
Most of those run against EF Core's InMemory provider, a stand-in database that lives inside the test process. It makes tests fast, but it is not SQL Server, and it quietly tolerates things a real database rejects. So anything that depends on real query behaviour is additionally checked against an actual SQL Server. That difference has caught genuine bugs here more than once.