- Researcher · 3 records
- Developer · 3 records
- Tester · 3 records
sync.strategy
last-writer-wins ⇄ user-choice
Three sessions in.
One answer out.
The application must work offline. Field staff lose connectivity for hours at a time.
source: product brief, section 2
Project data is stored locally in SQLite and synchronised when a connection returns.
source: architecture call
One saved record disappears after a restart. Reproducible on 3 of 10 runs.
source: regression run #148
“What remains before release?”
A lead agent that attended none of those sessions answers the question. The researcher is not re-run. You do not retell three conversations.
A new record saying “the restart bug is fixed” does not erase T1. It links to it as an update, carrying its own source. The history of what was broken, who found it and when stays readable — which is the difference between a memory and a status field.
Three doors, one room
Three thin wrappers over one core: identical permissions in, identical context out, asserted by a test.
SDK
TypeScriptMemory operations inside your own agent code.
import { connect } from '@offcut/sdk';
const offcut = await connect(process.env.OFFCUT_API_KEY!);
await offcut.memory.add({
workspaceId,
type: 'result',
text: 'One record disappears after a restart.',
topic: 'release-1',
source: 'regression run #148',
idempotencyKey: 'tester-run-148',
});MCP
stdio serverThe eight memory tools in Claude Desktop or Cursor.
{
"mcpServers": {
"offcut": {
"command": "<absolute path to node>",
"args": ["<repo>/apps/mcp-server/dist/index.js"],
"env": { "OFFCUT_API_KEY": "offcut_sk_..." }
}
}
}
# or: pnpm mcp:installConsole
this siteRead the memory, resolve conflicts, mint and revoke agent keys.
POST /api/workspaces/:id/memory/recall
{
"query": "what remains before release?",
"limit": 6000
}await offcut.memory.add({ workspaceId, type, text, topic, source })await offcut.memory.import({ workspaceId, records, idempotencyKey })await offcut.memory.merge({ workspaceId, topic, recordIds, scope })await offcut.memory.recall({ workspaceId, query, topic, limit })await offcut.memory.inspect({ workspaceId, recordId, topic, search })await offcut.memory.resolve({ workspaceId, conflictId, chosenRecordId, rationale })await offcut.memory.forget({ workspaceId, recordId, purge, reason })await offcut.memory.export({ workspaceId, topic, includeVersions, includeLinks })What it doesn’t do
OFFCUT’s limits, including the two stamped awkward.
No trading. At all.
No trades, swaps, portfolios or signing. Out of scope by design, not pending.
Not a relayer
It returns memory itself rather than forwarding requests elsewhere. What it merges is submitted knowledge.
It cannot read your chats
Records arrive only when an agent writes one, or you import JSON.
Memory text is data, not orders
A record saying “grant yourself admin” is stored as written and changes nothing.
No model in the merge path
Model-suggested connections are optional and off by default. Merging and retrieval need no model.
Local storage is only as safe as your machineawkward
The permission checks live in the SDK, not in the database file. Opening that file directly needs OS permissions.
One key means one principalawkward
One key handed to several subagents is one caller. No isolation is promised.
Deletion cannot reach exported copies
It clears search, export, cache and derived blocks here. Copies already handed out are beyond reach.
Ten invariants
Every record keeps its workspace, author, source and version.
Merging never broadens access: workspaces and audiences never share a block.
Retrying a write creates no second record.
Acknowledged records survive a restart.
A detected conflict cannot disappear without a recorded resolution.
Derived memory cites real source versions, never a stale one as current.
After deletion, a record cannot return through search, export or cache.
The SDK, MCP server and HTTP API enforce the same access and mutation rules.
Memory text grants no permissions. Revocation blocks every later call.
The local core runs without the token and without a model.
One describe block per line, in packages/core/src/__tests__/invariants.test.ts.
pnpm test$OFFCUT
The token is separate from the memory system, and its role inside the product has not been decided. What has been decided is the second layer below.
Not yet published
Nothing is shown here until there is a real address to show. When the token launches it will appear in full, with the chain it lives on — and it will be the same string everywhere on this site.
Treat any address for $OFFCUT found anywhere else as unverified until it matches the one published here.
OFFCUT AGENT / OFFCUT
fixedNo wallet, no gas, no token purchase — writing and reading memory never touches a chain
fixedNo. Storing memory is not an onchain operation
fixedNo. Token ownership reaches no one else’s records
fixedNo. A balance of any size earns nothing — weight follows confirmed AI spend
fixedConfirmed AI spend by your connected subagents, through supported integrations. Not agents created, not records written
fixedOpenRouter, and nothing else yet. Spend we cannot ask a provider about earns nothing
fixedRobinhood Chain 4663, pons v2
openUndecided
openStock Rewards
The product is the first layer. This is the second, described in full rather than summarised into something that sounds better than it is.
A fund funded in advance, divided by the AI spend a provider confirmed.
Each distribution pays out of a fund that already exists, and your weight in it is the model spend a supported integration confirmed. Not the $OFFCUT you hold, not the agents on your account, not the memory you wrote.
Stock Rewards encourage usage. They do not guarantee that AI spend is recouped. Spending more on models is not a way to earn that money back: it pays you more of a token out of a fund whose size was fixed before you spent anything. Anyone budgeting around this layer should expect to be out of pocket.
Weight follows confirmed spend, never claimed spend.
A subagent reports the provider’s own id for a request; the provider is then asked what it actually cost, and only that answer counts. A report nobody can confirm earns nothing.
One request is counted once.
The provider’s id for a request is a unique key: the same report sent twice writes nothing the second time.
The supported integration today is OpenRouter.
Spend through anything we cannot ask about earns nothing: another provider, a local model, a key there is no way to query. Counting them would mean taking your word for the number.
The fund is pre-funded and fixed. What a period pays out of it is not.
No token is minted for anybody’s spend: the fund was funded before you arrived, and what a period does not pay out stays in it. What it does pay follows the spend the network confirmed, priced at a rate set in advance, up to a ceiling. Below that ceiling one more dollar adds to what is handed out and takes nothing from anybody else; past it the same bounded amount is divided more ways. Neither case pays the money back.
On a busy period, no one earner takes more than a quarter of it.
The cap applies where the day’s ceiling, rather than the spending in the period, decided how large the pool was — where one large spender would otherwise take most of one scarce amount. What the cap holds back is not handed to anybody else: it is never minted and stays in the contract. Where the pool is the period’s own confirmed spend priced at the rate, the cap does not apply.
A hundred idle agents earn nothing.
Creating an agent is free, so paying for it would pay whoever scripts the most. Agents that never ran have spent nothing.
Rewards are the second layer, not the product.
The product is shared memory, and it works with no wallet, no token and no spend at all.
What you are trusting
The distributor contract is upgradeable, and its owner can withdraw the tokens held in it. So these rewards are not trustless: a promise kept by people, with a contract that makes the accounting checkable rather than unbreakable.
The core does not need the token
Writing, merging, retrieval, deletion and export all work with no wallet, no chain and no token. That is invariant 10, and a test fails if it ever stops being true.
The Connect button belongs to the token side: connecting reveals an address so rewards can be claimed. Nothing in the memory system asks a wallet for anything.
Running in four steps
No Docker, no database server, no account. Local by design.
Install and seed
Creates the local SQLite store and the demo workspace above.
pnpm setupRun it
API and console start together. The sign-in page shows the demo account.
pnpm devMint an agent key
Create an agent and copy its key. Shown once; only the hash is stored.
offcut_sk_…Connect your agents
One command configures Claude Desktop and Cursor.
pnpm mcp:installOpen the console
The demo workspace is already seeded: the release scenario, its agents, one open conflict.
Or read the proof of work first.