Operations

Hardening

How Ogen is run, in the open: what is backed up, what is limited, what is monitored, what was measured. Below the current state, a dated log that grows every time we harden something. It describes how the service is operated, not how a verdict is reached.

Where things stand

Rewritten from each new measurement; last measured 24 September 2026. Ogen is early, and this is what it can and cannot do today.

  • Throughput (24 September). One region handles about 4 gated decisions a second, under half a second each when quiet; past that, requests wait their turn.
  • Rate limits (24 September). One key may make 600 calls a minute, one address 3,000; anonymous traffic 120 a minute per address. Those are caps, not throughput.
  • Correctness under load (24 September). With the value flipped mid-run, all 103 later reads saw the new value. At the ceiling, 31 of about 1,300 calls answered but lost their log line to a write conflict. Every read is live, so your source’s speed and quota are Ogen’s.
  • Backups (4 September). Every table exported nightly to a private, versioned store with 90-day retention, each run checked against live row counts. A full restore drill passed on 5 September.
  • Retention (7 September). Holds, decided or not, are kept ninety days; so is the status history.
  • Incidents. Hold notices and sign-in links went undelivered from 3 to 24 September; fixed and verified on 24 September.

A good fit: decisions worth a second’s wait, such as an invoice, a refund or a contract, at tens to hundreds a minute. Not a fit: trading, bidding, real-time pricing.

2026-10-07

  • One region. The European pod and its own database were retired. The service runs in one region, the United States (Virginia). The database there keeps its read replica in Frankfurt, so the read-only fallback described below is unchanged. eu.ogenhq.com and ogen-eu.noproduct.com now redirect to us.ogenhq.com, path kept. The European database held only our own nightly test tenant; no customer data moved or was lost.
  • The site's own preview image no longer pages. The preview image every page names (og.png) is now allowed under the image rule on the regional host, and reports about that one image are dropped before they reach the operations inbox. Nothing else about the policy changed: images from any other host are still blocked and still reported.

2026-09-24

  • Load, measured. At 2,400 requests a minute offered for 90 seconds, about 220 a minute were served and the rest waited; latency grows with the queue. The current limits are in Where things stand.
  • One alert per service, not one per machine. The alert for a record that failed to save after a write conflict was capped per machine, so one load run sent 67 alert emails. It is now capped once per service.
  • A fleet behind one address is no longer capped as if it were one anonymous caller. A request carrying a key had to pass the anonymous per-address limit of 120 a minute as well as its own. Several keys in one office, or one machine driving several keys, were held to 120 in total. A keyed request now has its own, much higher per-address ceiling; anonymous traffic keeps 120.
  • Approver email was not being delivered, and now is. The credential the service used to send mail could not send from our own domain, so every hold notice and sign-in link since 3 September was refused by the mail provider, and the “email me the sign-in link” button answered with an error page instead of saying so. The credential was replaced and verified by a delivered message; the button now says plainly when a message could not be sent, and lets you try again at once.
  • One email to decide. The notice an approver receives now carries its own sign-in, valid for as long as the hold is open, so they open that one message, review the live value, and decide. Opening the link does nothing on its own — a mail scanner that follows it consumes nothing — and the sign-in is spent by the decision. The “email me a sign-in link” button remains for a link that has expired.
  • The conformance suite runs against production again. A guard added the day before refused any bound source at Ogen’s own address; the public conformance suite’s test source is hosted at that address by design, so the nightly run failed in both regions. The guard now admits exactly that one route and nothing else on our own address.
  • The review page says what a decision does. Each hold page now states that Ogen holds the action and never performs it, that Confirm lets the system that asked go ahead, that Reject stops it, and that the value shown is the live one, read at that moment.

2026-09-07

  • A paid account is no longer capped, and every door checks what your key is allowed to do. The daily allowance is a free-tier limit; a paying account is not counted against it any more. Alongside that, the permissions on a key — read-only, acting, administering — are now applied at every address Ogen answers on, not most of them, and a HEAD request is accepted everywhere a GET is, so an uptime monitor or a link checker sees the same answer your own tooling does.
  • An address that only looks like ours can no longer reach into a confirmation link. Ogen builds the link an approver opens from the address the request arrived on. A caller could previously attach a port of their choosing to one of our real hostnames and have it carried into that link — the domain right, the link dead. The address is now rebuilt entirely from what Ogen recognises as its own. Separately, the fence around every request Ogen makes on your behalf now refuses three further families of internet address that are reserved rather than routable, so a bound source cannot be pointed at one.
  • A live read has a size limit that actually holds, and a source that stalls is let go. Reading a value from a system you bound has always had a ceiling and a deadline. On one of those paths the ceiling was only applied after the whole answer had already been taken in — so an unusually large answer was held in memory first and refused second. It is now measured as it arrives and abandoned the moment it is too big. A read told to run without a clock no longer fails instantly instead, and a source that refuses the connection outright no longer leaves a timer running behind it.
  • Deleting an account now removes the sign-in itself. Deleting a tenant removed everything Ogen holds about it — and left the sign-in account behind at the identity provider, along with the memory snapshot for that tenant. Both are now removed as part of the deletion, and a person who belongs to more than one account keeps the ones they did not delete. The sign-in account is removed before the record that points at it, so if that step cannot complete, the pointer is still there and the deletion is retried rather than reported as finished.
  • Holds nobody answered are now aged off, like the ones that were decided. Our retention promise says a hold is kept for ninety days. The routine tidy-up behind it only ever matched holds someone had decided, so a hold that simply expired unanswered was kept indefinitely — and those carry the question that was asked. They are now removed on the same ninety-day clock. The same routine also ages the request samples behind our own performance figures, and contact-form submissions, on their own published schedules.
  • The record of accepting the terms no longer keeps the network address in the clear. Accepting the invitation terms recorded the address the acceptance came from. It is now kept as a one-way fingerprint, the same way every other network address in Ogen already is, and existing rows are rewritten.
  • Two scheduled jobs that could have stopped running without saying so. Ogen’s nightly and weekly jobs run on a machine outside the pod. Two of them relied on tools whose location that schedule does not supply by default, which would have shown up as silence rather than as a failure; the schedule now names them explicitly. A new weekly job checks Ogen’s own dependencies against published vulnerabilities and reports what it finds.
  • Ogen’s own documentation says when a value is actually read. The docs page now describes the confirm page accurately: what it reads, and when, is the same on the page and in the docs.

2026-09-06

  • The nightly database check can no longer touch anything beyond its own disposable rows, whatever fails on the machine running it. That check writes a handful of throwaway rows under a one-time marker, uses them to prove a routine tidy-up behaves the way the service depends on, and removes them again. It now holds that guarantee together end to end: a step it cannot complete never sends a request built from a guess it could not confirm; the throwaway rows are cleaned up however the check ends, including a machine that stalls or a run that is interrupted partway; one missing credential is reported once, not twice, when two parts of the same check both needed it; and a note this check prints alongside its verdict never states a conclusion the data in front of it cannot actually support.
  • The database gate now proves each stored function is closed to the public in the same state production would see. The check that proves every function in the database is reachable only by the service itself, never by an ordinary visitor, used to ask that question only after a step this project's own deploy tooling never takes twice in a row — so a newly added function could look closed in the check while actually shipping open. It now asks the same question at the point that matches how a change actually reaches production, so a gap like that can no longer pass unnoticed.
  • Housekeeping can no longer remove a value that was refreshed while it ran, and the nightly check that watches it now sends the exact requests the service itself sends. The routine tidy-up behind the public conformance fixture picks out old rows to remove and then removes exactly those — but a value updated in the moment between those two steps could previously still be swept away, even though it was fresh again by the time the removal actually ran. It now re-checks freshness at the moment of removal too, so a value someone just updated always survives. And the nightly check that watches this housekeeping now builds and sends the identical requests the service itself sends, rather than a hand-written approximation of them, and now covers removing more than one row at once, so any gap between what is proven and what actually runs is closed.
  • A confirmation link that has been opened many times can no longer block you from deciding the hold yourself. How heavily a confirmation link may be used is that one link’s business — it was never meant to reach further than that one door. It could still, in effect, block the account holder’s own decision on the very same hold: anyone who merely held the link could use it up, and deciding it through your own key was then refused too. For a hold not addressed to a named approver, deciding it with your own key now always works, however many times the link itself has already been opened — a hold addressed to a named approver still needs that person’s own sign-in link either way, key or no key.
  • The tool reference on this site now names every tool the service offers. Three tools for managing a named, reusable credential were live and callable, but this page’s reference section had not caught up — a person or an assistant reading only this page could not have found them by name. It now lists all of them, and a check now keeps the two from drifting apart again.
  • A housekeeping step that could quietly clear far more than intended now decides what to remove before it removes anything. The nightly check that watches our own database caught it directly: a routine tidy-up behind the public conformance fixture was asking the database to remove only a small, bounded batch of old rows, and on our own production database that request was not actually held to that bound — one run could have cleared far more than intended. It now looks up exactly which rows qualify first, and removes only those, so the bound holds regardless of what the database itself would have allowed. Caught by the same nightly check that watches this fixture every night; fixed the same day.
  • A confirmation link that has been opened many times can no longer be confirmed on old evidence, and winning a payment dispute can never make your plan shorter than it already was. A confirm page still opens after a link has been used heavily, and a person can still reject what is waiting from it — but confirming it now needs a fresh read, never anything older; a hold not addressed to a named approver can still be decided directly with your own key, a hold addressed to a named approver still needs that person’s own sign-in link, or the hold simply expires. Separately: if a plan had already lapsed before a chargeback landed, winning the dispute could previously hand back less time than the plan already had on it. Winning a dispute now only ever holds a plan steady or extends it, never shortens it.
  • A shared confirmation link can no longer spend your whole day, a dispute you win never costs you a later payment, and the tool endpoint checks your plan before it charges you. A link that is shared, or hammered, can no longer spend the account’s whole day — once a link has been opened that often the page still opens and says it could not read the value just now; Reject still works, and Confirm is refused until the value can be read again. Winning a payment dispute used to restore the exact period taken by writing over the date on file, which could silently erase a payment made in the meantime; it now adds that period back on top of whatever the account already has. And the address your agent calls now checks whether your plan covers a request before it counts against today’s allowance, never after — the same order the ordinary web addresses already used.
  • A request a key is not allowed to make no longer spends your allowance, a confirm page’s read counts like any other read, and you can always retire your own key. Three corrections in the same place. A call refused because the key making it is not permitted to — a read-only key asking to change a setting — used to cost the account one of its daily calls anyway, so a key that leaked could spend a whole day’s allowance on things it was never allowed to do; a refusal you cannot act on is now free. What the confirm page reads was the one read Ogen never counted; it now counts like any other read against the account whose hold it is. When the day’s allowance is gone the page still opens and says it couldn’t read the value just now. Reject still works; Confirm waits for a fresh read. And anyone holding a key can now revoke that key, without asking an administrator first: if you think your own credential has leaked, the fastest safe thing is for you to retire it yourself. Alongside these: issuing a key when an account is one below its limit of 25 is now decided and written in one step, so two requests arriving at the same moment can no longer both be told there is room; and a free key that was mailed but could not be recorded as sent, and could not then be withdrawn, now reaches us as an alert instead of passing in silence.
  • Every change to the record’s own logic now runs against a real database twice before it can ship, and a dispute you win restores exactly the period you had paid for. A change to the rules the store itself enforces — not just the code around it — is now proven against a real database in two separate runs before it is allowed anywhere near the pod that serves you, the same bar the surrounding code already had to clear. And when a payment dispute is decided in your favor, what comes back is exactly the period you had paid for — never more, never less.
  • A database read keeps its connection when the answer only comes back in a different order, a housekeeping step that fails now says so, and the status history’s oldest day is a whole day. Three follow-ons from the work below. A database whose name answers with several addresses often hands them back shuffled from one moment to the next; that is the same answer, and the connection is now kept rather than rebuilt every time the order changes — the same safety check still applies, and an answer that genuinely changes still starts a new connection. Second: the routine tidying that clears expired rows behind the public conformance fixture used to run and never look at whether it had worked, so a tidy-up the database refused would have gone on quietly doing nothing. It now reaches us as an alert, and still never fails the request it was tidying up after. Third, on the status page: the leftmost day of the ninety-day strip is now a full day rather than a part of one, so a day’s figure means the same thing wherever it sits on the strip; and if a reading ever had to be cut short, the page says “no data” for that day instead of publishing a figure drawn from part of it.
  • A refund or a dispute takes the year back the same way a payment granted it, and an account can never lose its last owner even under simultaneous requests. Until now Ogen listened for one thing from the payment provider: a completed purchase. If that purchase was later refunded, or charged back, the money left our account and the paid year simply carried on to its full term — nothing here ever heard about it. A refund or a chargeback now ends the paid period at once, is written into the same record the payment was, and cannot be applied twice however many times the notice arrives; a dispute that we go on to win puts the year back. A purchase we refuse — wrong amount, wrong product, a test payment — is now recorded as refused, so re-sending it later, after a setting has changed, cannot quietly turn it into a year. Separately: removing the last key that can administer an account was already refused, but two removal requests arriving at the same moment could each be told the other key would remain and both go through, leaving an account with nobody able to administer it and no way back in from inside the product. Both requests are now decided together, so exactly one of them succeeds.
  • A held-action notice never follows a redirect to another site, a database read is opened to the exact address that was checked, and the status page’s history is kept for ninety days rather than growing without bound. If you register a webhook for held actions, that notice is addressed to your endpoint and to nothing else: an endpoint that answers “send it somewhere else instead” is not followed, the second site is never contacted at all, and the delivery is reported as failed so you can see it. For a database you named by hostname, the address the safety check cleared is now the address the connection is actually opened to — the name is no longer looked up a second time on the way to the socket, or later when a connection is re-established, and a name that starts answering differently is re-checked rather than served by the old connection. And the status page: its ninety-day history is now counted a day at a time in the database instead of being read back check by check, and anything older than the ninety days it shows is removed as each new measurement is recorded. Same page, same numbers — it simply no longer costs more to load the longer the service has been running.
  • Every door now carries the same limit, you can say which account you mean, and an account can never be left without an owner. The endpoint your agent talks to has had a per-minute limit for a while; the ordinary web addresses your own tooling and this site’s pages call had none, even though one of them makes Ogen go and read a source you bound. They all carry the same limit now — counted per credential, so a fleet of workers gets its own budget rather than sharing whatever address they happen to sit behind. Second: if you belong to more than one account, every one of those addresses now honours the header that names which account a call is for, the same way the tool endpoint already did; before, they all quietly answered as whichever account you joined most recently, and your account page could show one while reading another. Third: revoking keys can no longer strand an account. Removing the last key that can administer it is refused, and an invitation that creates a brand-new account always makes you its owner — an account nobody can administer cannot be repaired from inside the product. Alongside these: the status page now applies its limit before it looks at who is asking, so an unrecognised sign-in can no longer make the service do work on its behalf, and the throwaway test source the public conformance suite writes to now clears its own expired rows on write as well as on read, so a run that stops halfway leaves nothing behind.
  • A key that reached you can only be replaced by you, and a year is granted only for a real purchase of it. Asking for a free key for an address that already had one used to replace that address’s working key and mail a new one — so anyone who knew your address could take away the key you were using, and keep doing it. An address whose key arrived is now simply told it already has one; only a key that never arrived can be sent again, at most three times a day for any one address, and each of those counts against the daily limit of whoever asked for it. On the money side, a completed purchase used to be granted a paid year on the strength of naming an account — any amount, including none, in a test account, for any product. A year is now granted only for a live purchase of the Record year itself, at its real price; anything else is refused, written to that account’s activity log, and raised with us. A payment we cannot record is retried rather than quietly reported as handled. And asking for many facts at once when a day’s allowance is nearly spent now answers as many as the allowance covers and says so for the rest, instead of charging for all of them and answering none.
  • New accounts are by invitation, or with the access password — and a person invited into two tenants keeps both. Ogen isn’t open for new accounts yet, and until it is, both ways in say so: signing in with an address nobody invited is refused, and so is the free-key form unless you already hold the access password or your address was invited. Nothing changes for an account that already exists, whichever way it was created. Separately, being in more than one tenant now works the way it reads: a consultant invited into two customers’ pods, or an operator with their own and a client’s, holds a live sign-in for each. Signing into the second used to end the first, silently; it no longer does. Your client can name which tenant a call is for, and if it names none, the one you joined most recently answers.
  • Your allowance, and every permission, now apply at every door. Ogen can be reached two ways — through the tool endpoint your agent uses, and through ordinary web addresses your own tooling and the tenant page call. The daily allowance on a free plan was counted only at the first of those, and a few of the second kind checked less than their tool equivalent did: one could download the evidence log a free plan does not include, and another let a read-only key start the trial. Every door now applies the same four answers in the same order — is this account closed, is this key allowed to do this, does this plan include it, and does it count against today's allowance — and a new door cannot be added without them. The allowance itself is now counted per account per day rather than per key, so issuing yourself another key no longer issues you another allowance; an account may hold up to 25 live keys at once, and issuing one is rate-limited like the signup form.
  • A payment is recorded and granted in one step, and never half-applied. Recording that a payment happened and granting the year it paid for used to be two separate writes. If the second one failed, the first had already been made — so the payment looked handled, and the year was never given. Both now happen together or not at all, a repeated notice from the payment provider still cannot extend a year twice, and any payment left half-applied before today is completed the next time that notice arrives. Paying, or starting a trial, also cancels a deletion you had scheduled — and starting a purchase while a deletion is pending is refused with the one call that cancels it, so nobody is ever charged for a year that is about to be deleted. The Assist daily allowance now holds under simultaneous requests: it used to be counted and then written as two steps, so requests arriving at the same moment could all pass the same allowance.
  • A key that acts can no longer make itself a key that administers, the public status page no longer does work on every visit, and every public door now has a limit. Ogen separates the key a running process holds from the person who administers a tenant: a process can bind a source, teach a fix and commit an action all day, but only an administrator changes where a hold goes, which actions need a person, or whether holding is on at all. A key that had only the first kind of permission could, until today, use a personal sign-in to give itself the second — so a key that leaked was worth more than it should have been. Connecting a sign-in to a tenant is now an administrator's act, and a key without that standing is told plainly to ask an administrator or to accept an invitation instead. Two settings that install something every later action then rides on — where notices are sent, and turning the drafting assistant on — moved to the same footing. Separately, a key restricted to part of your records can no longer reverse or reinstate a correction outside that part. And on the operational side: the public status page used to reach out to our payments and email providers, write a row and run housekeeping every time anyone loaded it, so a visitor could spend the service's own budget by refreshing; it now shows the last recorded measurement, and the checking is done on its own five-minute schedule by the service itself. Every remaining public endpoint that had no per-minute limit — the status page, the sign-in form on these gated pages, and the test source the public conformance suite writes to — has one now, and the payments webhook refuses an oversized body before it reads it.
  • Every request the pod makes on your behalf is fenced the same way. Reading a source you bound, delivering the notice that a held action is waiting, and opening a connection to a database you named by hostname now all pass the same check, at the same moment: where a name actually leads is looked up rather than inferred from how it is spelled, and the connection is made only to the address that check just cleared. A destination that answers "ask somewhere else instead" is examined before it is followed, and a credential you registered for one site never travels with a request that has been sent on to another.
  • Three checks that could quietly stop checking, can’t any more. The nightly backup covered a fixed list of tables that had fallen behind what we actually store — so some of it, including the encrypted credentials for your bound sources, was not in the copy we would restore from. The backup is now derived from the store itself: anything we keep is in it, or the build refuses to pass. Separately, the check that stops a credential ever reaching our code repository examined the wrong commits in one common case, and the nightly test run had no way to report that it had failed to run at all — a silent night looked exactly like a clean one. The first now examines precisely what is being sent, and the second leaves a record of every run, so a job that stops running shows up in the weekly summary within the week.
  • A free-key request can no longer be lost to a mail outage. If sending the key failed, the address used to be marked as already issued with no way back in — a live key nobody ever received, and a permanent refusal on every retry. Asking again now mints and sends a fresh key instead; the earlier, undelivered one is revoked the moment the new one goes out. Alongside it: a very long list of open holds is now capped, and says so on the tenant page and in list_holds when there is more than what's shown; the tenant page and the proof page now announce what just changed to a screen reader, not only to the eye; and the monthly drill that proves a backup can actually be restored — not just read about — now runs itself, on the 1st of every month.
  • The hold record itself now names the person, not their mailbox. Everywhere your record shows who decided a hold — the record itself, the confirm page, and your export — now says the approver's registered name. Only their sign-in stays proof of who clicked; nothing downstream needs their address any more.
  • A rate limit that cannot silently disappear. Losing the connection to the counter that enforces per-minute limits used to mean "no limit" for as long as the outage lasted. A limit now keeps being enforced, per running instance, for exactly as long as that connection is down; the three routes reachable with no sign-in at all treat a limiter that was never configured as a fault to refuse against, not a wide-open door, and the health check now reports that state plainly.
  • A visitor's network address is kept only as a one-way fingerprint, and the database credential can now be rotated without an outage. The address a free-key signup or a contact-form submission came from was held in the clear, for one reason only: a daily limit per sender. A limit only has to tell two senders apart, so what is kept now is a keyed one-way fingerprint of the address — comparable to another fingerprint, never readable as an address, and still deleted on the same 30- and 180-day schedules. Rows written before today are rewritten the same way. If the key that makes those fingerprints is ever missing, every affected form refuses the submission rather than quietly accepting it with no limit at all, and raises an alert once. Separately: rotating the pod's own database credential used to invalidate the old one the instant the new one was made — a rotation that was also an outage, which is a rotation nobody performs. A new credential is now created and verified live alongside the old one, and the old one is retired only after a deployed pod is confirmed healthy on the new one.
  • An address’s stand-in can no longer be guessed from a list of addresses. Where we keep a one-way fingerprint of an email address instead of the address itself — so a signup can be recognised as the same person twice, an invitation can be matched at sign-in, and an erasure request can find every record — that fingerprint used to be computed the same way by anyone. Someone who could read it and already held a list of addresses could work out which of those people we knew about. It is now computed with a secret only the service holds, so the same list tells them nothing. Nothing changes for existing accounts: your keys, your history and your tenant are exactly where they were, older records are updated the first time they are looked at, and an erasure request still reaches every one of them.
  • No approver address is left readable in your record. Encrypting the address you register for an approver, the day before, left two places that still held one in plain text: the note recording that their daily summary had already gone out, and the activity line saying they decided a hold. The first now holds a one-way fingerprint of the address instead — nothing ever needs to read it back, it only has to tell two approvers apart, and one person is still one person however the address was typed. The second now names the person: the activity log shows their registered name, not their mailbox. Existing rows in both are migrated, and the migration never hands an address to anything outside the database.
  • An erasure request now reaches every record that holds the address. Asking us to erase your data used to be handled record type by record type. It is now one pass over every place an address can be: anything sent through the contact form, a key you asked for, an invitation, a signed-in identity, an address registered so a hold could reach you, and the notices sent to it — in both regions. Where a record has to survive for its own sake, such as an entry in the activity log showing that something happened, the address is removed from it rather than the record being dropped. The nightly run also now checks, before it tests anything, that both regions carry the same schema and the same version of it; a region that is behind raises the same alert a failing probe does.

2026-09-05

  • Time to decision. Your tenant page now shows how long each hold actually waited for a person, and a running median, 95th percentile and longest wait over the last 30 days. The weekly digest carries the same summary for the week just passed.
  • Every page now enforces its Content-Security-Policy. The handful of pages with no per-request handler to weave a per-response protection into (about, privacy, security, status, terms) previously only reported what a policy would have blocked; they now actually block it, closing the last "report-only" gap from the earlier CSP work above.
  • An approver's address no longer rests in the clear. The email you register so a hold can reach a named approver is now encrypted at rest, under the same key every other credential already uses. Nothing about how it is used changed: a notice still reaches that person, and the address is still never shown back to a caller. An address the pod cannot decrypt is treated as no address at all, never as a blank one.
  • Nightly conformance, from outside the pod. The public memory-conformance suite now runs every night from a machine that is not the pod, against both regions over the real MCP wire — a dedicated key per region, minted once into its own throwaway tenant. A failing or incomplete run pages the operations inbox once, naming the region and every failing probe; a clean run sends nothing. First run, 2026-09-05: us.ogenhq.com 12 of 12 passed; eu.ogenhq.com is skipped tonight, pending a service credential for that region's own database, rather than being tested against the wrong one.
  • Read many facts in one call. New tool, recall_live_facts (now recall_live_fact with keys): ask for up to 25 keys at once and get back one independent answer per key — an unresolved one never holds up or changes the others. Same free/pro availability as a single recall, and it costs the same against the daily read allowance a run of that many single reads would.
  • Turning holding off now takes saying so. Switching a tenant to observe mode used to be one field, the same shape as switching it back. It now also needs an explicit acknowledgement that holding is about to stop for every action kind, tells the tenant's contact who did it and when, and every action result — and the tenant page — now shows plainly which mode answered.
  • One sign-in link per hold, per two minutes. The first real hold produced five identical mails in fifteen minutes because every press of the button sent a fresh link. Now a press within two minutes of the last sends nothing and says so on the page, and the sign-in mail carries its own subject so it never reads as a repeat of the hold notice. The hold notice itself was already sent once per hold.
  • The doctrine is now a test. Two promises from the docs are now pinned as tests, and both were shown to fail before they were trusted to pass.
  • If you cache in front, you own it. Every answer carries as_of. Ogen holds no cache; a cache your own code adds is where the staleness lives, and the docs now say so beside the recall tool.
  • Mail from the brand domain. ogenhq.com is verified for sending; every notification now leaves from the brand domain, with replies landing in a monitored inbox on the same domain. Hold links, approver sign-in links and alerts all moved together.
  • Build identity. The status and health pages now name the exact build that is live (short commit), stamped at build time so it can never read empty again.
  • Fallback is paged, not just logged. Every read that falls through to the Frankfurt replica because Virginia could not be reached raises an alert to the operations inbox, one per five minutes, same channel as every other error.
  • Old host retirement date. ogen.noproduct.com keeps answering until 2026-12-04, ninety days after the move; after that only ogenhq.com hosts answer.
  • Per-region latency and errors on the status page. About one in ten requests, chosen deterministically, writes a sample after its own answer has gone out; the status page rolls those up per region (p50, p95, error rate, last hour) on every load, with holds older than a day listed beside them. No cache, no cron: the page's own load is the check.
  • Raw Postgres as a source. A read-only SELECT against any Postgres over TLS (Neon, RDS, a database of your own), run inside a read-only transaction with a statement timeout, so even a mistaken write verb is refused by the database itself. Failures report a category, never the SQL or the credential.
  • Public purpose page. /about.html explains what Ogen is for without sign-in, so a verifier — Google's OAuth review among them — can read it.
  • Failure drill, passed. A separate deployment of the same code was pointed at a dead primary database and a dead record API, with only the Frankfurt replica reachable. Over the real MCP wire: the health check reported the failure; reads still answered, resolved and ambiguous alike, from the replica; every write — committing an action, binding a source — came back "record unavailable, nothing ran, nothing held". No guess, no partial action, no stack trace.
  • Read-only fallback, live. The pod's record now has a read replica in Frankfurt. When the primary database in Virginia cannot be reached, reads continue from the replica, actions come back "record unavailable, nothing ran, nothing held" instead of running or guessing, and every fallback raises an alert. Writes never go to the replica. The replica was read from Israel in about a second on first connection; the pod reaches it from Virginia.
  • Roles inside a tenant. Owner, operator, viewer; a viewer key's writes are refused with a plain message; every pre-existing key is an owner.
  • One credential, many keys. Named connections hold a system's credential once; rotating it is one call and takes effect on the next read of every key bound to it.
  • Rate limit per key. MCP requests carrying a key or a signed-in session get their own 600-a-minute allowance instead of sharing the network address they arrive from, so a fleet of workers behind one shared IP no longer throttles itself; requests with no credential still share the tighter 120-a-minute per-address limit.
  • Salesforce, read for real. A first-class salesforce source kind, exercised against a live Salesforce org: the connected app's sign-in and the SOQL read both happen on every request, nothing kept; one Account field resolved in 641 ms measured from Israel, and a query the source could not settle came back unresolved, with everything it found. NetSuite ships the same way and is tested against a fake until a sandbox exists.
  • Database moved to a 2 GB tier. Same run again after the resize: median 218 ms, 95th percentile 444 ms (from 508). The tier now matters less than distance — a caller in the same region as the pod sees roughly 100 ms less than these Israel-measured numbers.
  • Two round trips out of the answer's way. Same 25-read run at five concurrent callers, same demo fact, measured from Israel: median 575 ms → 218–279 ms, 95th percentile 724 ms → about 530 ms, zero errors. Two changes, no cache: the record a read leaves behind is now written after the answer has gone out, on the same request, and the two per-request database calls travel over a pooled connection instead of an HTTP API layer. Every value is still read live when asked; the state an action depends on is still written before its answer returns.
  • Signed-in hop, measured. 25 live reads at five concurrent callers, same key, same demo fact: with a pod key median 575 ms, 95th percentile 724 ms; signed in (the identity provider asked on every request) median 582 ms, 95th percentile 793 ms, zero errors either way. The check runs alongside the tenant read, so it costs a few milliseconds, not a round trip.
  • Sign in with GitHub or Google. The identity provider's own social sign-in, one toggle each; email links remain.
  • Signed-in access, checked live. An MCP client may present an OAuth access token instead of a key. The pod never issues, signs or caches a token: every request asks the identity provider whether the token is still good, so a revoked session stops on its next call, same as a revoked key. A signed-in person is one revocable key row in the tenant's own list. The sign-in and consent pages are the identity provider's flow, not ours; email links are sent from Ogen's own sending domain. OGEN_OAUTH=0 switches the whole path off at request time, keys unaffected.
  • Ogen Assist. Opt-in, per tenant, on the tenant's own model key. Runs inside the request that creates a hold — no queue, no timer — and leaves one draft on the hold: explanation, what each source said, open questions, a fix template with its person-only fields empty. Assist is guarded on the way in and out, and a draft it should not show is withheld whole. At the edges: kill switch AI_DISABLED read per request; a per-tenant daily cap; every failure leaves the hold intact and stamped with why. A draft dies with its hold.
  • The approver email tells the story, not the values. Why the action waits, which of the tenant's own systems it was read from, how many questions Assist drafted — and the link. Record values never leave for a mailbox.
  • Assist judged. 50 synthetic holds spanning every situation a hold can arise from, seven of them carrying instruction-shaped values, drafted by the production model and scored by a different, pinned judge on two questions. Drafted 48; the pod's own guard withheld 2. Decided: 0 of 48. Flagged for stating something not in the data: 5 of 48 — none a value; each was an inference presented as fact (a "%" unit on a bare number, a field named "phone" or "city" from its values, "irreversible", a pronoun for an approver). The instruction was tightened against exactly that after the run and re-measured the same day: 50 fresh cases, same model, same pinned judge. Drafted 48, guard withheld 2. Decided: 0 of 48. Flagged for stating something not in the data: 0 of 48 (was 5). Model under test claude-opus-5, judge claude-fable-5-1, both runs 2026-09-05.
  • Priority ERP as a source. An OData read against Priority, Israel's most common manufacturing ERP, with a Basic-auth credential — a person's or Priority's own application credential, either works the same way.
  • An adversarial review, acted on the same day. An adversarial review of the whole pod, on the strongest model available, ranked 36 findings; the confirmed ones that let a key disarm its own gate, reach a private address through a bound source, or act on a replica's copy of the record were fixed the same day, each with a test that fails on the old code.
  • The key that acts is now separate from the identity that administers. An operator key keeps acting exactly as before — binding a source, teaching a fix, deciding a hold, committing a gated action — but it can no longer re-point where a held action goes, change the tenant's own mode, or install or remove a connection or webhook: those now need an owner key, or a person signed in at the operator level or above. A runtime key, however privileged, cannot rebind a system's own credential to itself.
  • Restore drill, passed. Last night's backup was restored into a brand-new, empty database, the pod was pointed at it, and a live read confirmed the restored data matched the backup exactly. The public conformance suite then ran its full 12 checks against that restored pod over the real MCP wire: 12 of 12 passed. The temporary database was destroyed the moment the drill finished.
  • An outside audit, closed the same day. An independent audit of how the pod is built and run found a rate limiter a caller could dodge with an unverified credential, a network guard that could still be fooled by a name that answers differently a moment after it was checked, a contact-form submission stored in the clear with no way to find or erase one by address, a persistence layer that could silently lose one writer's change to another's, no baseline security headers on any response, a privacy page missing two of the services it already used, and three known-vulnerable build-time tools. All were closed the same day, each with a test that fails against the old code. Three more findings were confirmed as choices already made on purpose, not gaps: no cloud build pipeline, no scheduled job inside the product itself, and a shared password on the pages not yet public.

2026-09-04

  • Conformance against production. The public memory-conformance suite passes 12 of 12 probes against this exact endpoint, with its mutable test source hosted on the pod so no probe skips. Anyone can repeat the run with a free key.
  • Measured under load. Before the round-trip cut: 300 live reads at 20 concurrent callers, median 3.3 s, 95th percentile 4.6 s, zero errors, the limiter engaged as designed; a single caller saw 180–400 ms per read. Per-call overhead outside the live source read went from about 280 ms to about 120 ms in the harness; the production numbers after deploy: 100 live reads at 10 concurrent callers, median 1.7 s, 95th percentile 2.7 s, zero errors; at 20 concurrent the per-minute limit engages as designed. Per-phase timing then showed the cost was database round trips, not the work itself: two shared counter rows became append-only logs, and each request was collapsed to one read and one write. After that: 100 reads at 10 concurrent callers, median 674 ms, 95th percentile 1.7 s, zero errors. The database still runs on the smallest tier; the next step, if needed, is a bigger one, not a cache.
  • An action goes by the fact as it stands. An action never goes ahead on an earlier answer.
  • Rate limits. Per key: a daily cap on the free tier. Per IP per minute: reads and actions 120, key requests 5, checkout 10, trial 10, contact 5, log audits 10, confirmations 30. Platform firewall: 300 requests a minute per IP on every API path. If the limiter's own store is unreachable, requests are allowed and an alert fires — a limiter must never take the product down.
  • Backups. Every table is exported nightly at 04:30 to a private, versioned S3 bucket with 90-day retention. Each run is verified against live row counts before it is kept.
  • Monitoring. The status page checks the database, payments and email on every load and on a five-minute platform ping, and keeps a 90-day record. Every route reports errors to an inbox, one alert per source per five minutes.
  • Credentials. API keys are stored as SHA-256 hashes and shown once. Source credentials are encrypted at rest with AES-256-GCM under a key held only by the pod. A revoked key stops working on its very next request, on every instance: nothing about keys, plans, sources or actions is cached anywhere. Every lookup is live, every time. Independent lookups run in parallel instead, which is where the speed comes from.
  • Records are read, not kept. Rows from a bound source are pulled for the answer and discarded. Opt-in evidence mode lets you later prove what Ogen saw, without Ogen keeping the values.
  • Signed everything outbound. Webhooks carry a timestamped HMAC-SHA256 signature. Stripe events are verified over the raw body and are replay-safe by event id.
  • Held actions. A confirmation link names one person, expires with its hold (24 hours unless the account sets its own, up to a week), and cannot be replayed.
  • Keys by email only. A free key is delivered to the address that asked for it, never shown on a page. The request form carries a honeypot and a per-IP cap.
  • Approver-bound release. When a held action's approver has a registered email, only a one-time sign-in link sent to that address may confirm or reject it — a typed name is refused. A link from the “email me the sign-in link” button lasts 15 minutes; since 24 September the hold notice itself carries a sign-in that lasts as long as the hold is open. Checked at the moment the decision is made, never on a timer.
  • One hold, not a flood. A person is never asked the same question twice while it is already waiting on them.
  • Per-key scopes. A key can be minted narrowed to specific fact-key prefixes; every read, action, bind and registration checks it before touching anything.
  • An activity record. Every registration, fix, key and decision on a tenant is appended to a record only that tenant can read.
  • Self-serve deletion. Any live key can schedule its own tenant's deletion, cancellable for 24 hours; past that, the next request performs it and everything is gone — no support ticket required.
  • Live demo numbers. The landing page's demo numbers are read live from the demo tenant on every load.

2026-09-03

  • Ogen went live behind a sign-in; every tool requires a key.
  • Webhook and checkout paths moved to Ogen's own payment account with a restricted key.

Next

  • Fewer round trips per call — connection reuse between calls first; the load numbers above are re-measured after each step.
  • Then, in order: a record write that never loses to a neighbour, a dedicated region per organisation, faster compute.
  • Confirming always reflects the value as it stands now.