
On 5 October, Michael Sutton asked Kaspa users a question instead of giving them an answer (X post).
He wrote that there are basically two kinds of covenants. Some are about the KAS they protect. Others are about the information they carry. Then he stopped and asked people to explain the difference in their own words, both the idea and what it means technically.
This article tries to answer him. The answer turns out to be one simple test: what happens if someone copies it?
The same morning, Sutton also fixed a hole in a demo exchange written in Argent, the contract language he leads. A single missing check would have let an attacker take one of the exchange's two piles of coins. That fix shows why the answer matters. You do not need to know how to code to follow any of this.
First, what is a covenant?
Normally, KAS sits at an address that belongs to a key. Whoever holds the private key can send the coins anywhere, at any time. The network checks only one thing: is the signature right?
A covenant adds rules to the coins themselves. The network checks the signature and also checks the rules. If a transaction breaks a rule, the network refuses it, even if the signature is perfect.
This became possible on Kaspa mainnet with the Toccata upgrade. Builders have been trying it out ever since, and this week many of them noticed the same surprising thing. Kaspa Silver described it well:
An address with no private key and no seed phrase sounds strange. Nobody owns it in the usual sense. The coins inside can move only in the ways the written rules allow.
Kind one: a safe with rules
The first kind protects KAS. Think of a safe with instructions on the door.
- "These coins cannot move before 1 January."
- "These coins can only go to these three addresses."
- "Two of these three people must agree before anything moves."
Time locks, escrow and vaults all belong here. The covenant looks at a spend and asks one question: does this follow the rules? Yes or no.
A Kaspa user called brt2412 showed why this matters. He built a test vault on testnet, the practice network where coins have no value. Then he played the thief. He used the vault's own private keys to try to take the coins. The vault let the withdrawal start, but the coins had to wait the one hour delay he had chosen before they could move.
In a normal wallet, a stolen key means the coins are gone in seconds. In a vault like this, a stolen key buys the thief a waiting room. That waiting time is the owner's chance to react, for example with a separate recovery path. As brt2412 put it, "My money is now programmable with self-enforcing spending rules." A day later he said several of his test vaults now work from start to finish (X post).
Kind two: a shared notebook
The second kind is about information, not only about money. Sutton says it forms a "mini social consensus".
Think of a notebook that many people share. Each new page must follow from the page before, by fixed rules. You cannot tear out a page. You cannot write whatever you like on a new one.
A name service is a good example. It must remember who owns which name. A token must remember who holds how much. Each new transaction must prove that the new page is a correct next step from the old one.
This week gave us a live example. Supertypo added a marketplace to dotk, the Kaspa name service, so people can list names for sale and buy them (dotk.name).
Notice what he said first. He was nervous to add it, because covenants "are a bit tricky to get right". Remember that sentence for later.
Sutton says notebooks like this can already carry tokens, exchanges and lending (reply). So this second kind is where most new Kaspa apps will live.
The real difference: what happens if someone copies it?
Here is a simple test. Take each kind of covenant and imagine someone copies it, rule for rule.
Copy the safe. brt2412 could publish the exact rules of his vault tomorrow. You could put the same rules on your own KAS. Your vault would protect your coins. His vault would still protect his. Nobody is fooled and nobody loses anything, because KAS is KAS. The network already knows which coins are real.
Copy the notebook. Now imagine someone copies the dotk name service, rule for rule, and starts their own notebook. On page one they write: "kaspa.k belongs to me." The rules are identical. The pages look identical. If your wallet cannot tell which notebook is the original, the name means nothing, and neither does any token kept in a notebook.
That is the difference. Copying a safe is harmless. Copying a notebook is forgery.
Sutton said almost exactly this the same night, replying to a question about time locks.
In his words, "you can clone my script over your kas and i couldn't care less". But when the information is the valuable part, "you start caring about imitation/forgery". And that, he wrote, "is why toccata introduced covenant ids and the notion of a covenant lineage" (reply).
How Kaspa stops fake notebooks
So the real question for kind two is not "are the rules good?" It is "is this the real notebook?"
Toccata answers it with a covenant ID. Think of it as a birth certificate plus a family tree.
The birth. A covenant ID is a fingerprint made from two things: one specific coin that gets spent to create the covenant, and the covenant's first pages, meaning its starting rules and values. On Kaspa, a coin can be spent only once. After that it is gone forever. So only one notebook can ever be born with that fingerprint. The design document says it plainly: "outpoints never repeat, so covenant membership is not freely choosable" (KIP-20). An outpoint is just the technical name for one specific coin.
The family tree. After the birth, the network lets a coin carry that ID only if it continues directly from a coin that already carries it. Every new page must name a real parent. Sutton calls this "provable lineage" (reply).
Put together, a fake notebook cannot even be created. KIP-20 compares this with older ideas, which could only make a forged covenant coin "unspendable". Covenant IDs make it "uncreatable in the first place" (KIP-20).
So when your wallet looks at a name or a token balance, it does not need to trust whoever shows it the notebook. It can check the ID. Copies can exist, but they can never carry the original's ID.
A birth certificate you can actually read
A covenant ID proves "this is the one real notebook". On its own, it does not tell you in plain words which rules the notebook follows. The ID is only a fingerprint.
That is the gap that covenant genesis proofs fill. They link an ID back to the source code, the settings and the starting values that produced it. Argent added them on 4 October (code change). We wrote about the proposal the day before (our article).
Sutton has said he expects "every stateful covenant" on Kaspa to provide such a proof eventually, and he wants explorers to show them (X post). Then "this app was audited" becomes something you can check. You compare the audited code with the code that actually created the ID.
One detail matters here. When someone suggested that the birth record is the key idea, Sutton pushed back gently. The birth record is meaningful "only bcs the covenant id gives us provable lineage" (reply). A birth certificate is only worth something if nobody can fake being the child.
Privacy is both kinds at once
The line between the two kinds is not always sharp. Olaf Weller is working on optional privacy for KAS. A private pool protects KAS, so it sounds like kind one.
But Sutton replied that real privacy needs a shared pool "with the internal split hidden by everyone from everyone", and keeping track of who owns which hidden part needs state (reply). So a private pool is a safe and a notebook at the same time. He added that "the same thing happens with based apps in general".
A simple way to remember
Safe with rules
- Protects: KAS
- Asks: "Is this spend allowed?"
- If someone copies it: harmless
- Examples: vaults, time locks, escrow
- Hardest part: choosing good rules
Shared notebook
- Protects: information, such as names or balances
- Asks: "Is this new page a correct next step from the real notebook?"
- If someone copies it: a forgery, which covenant IDs stop
- Examples: names, tokens, exchanges
- Hardest part: keeping every page correct, forever
Now the hard part: one missing check
An exchange is a shared notebook with money inside. That makes it one of the hardest things to build.
Sutton keeps a demo exchange in the Argent Playground, a collection of example apps. Each trading pair has two piles of coins, one for each asset. Call them pile A and pile B. A part of the exchange called the Pair is in charge of both piles.
Here was the setup. Each pile said: "I may move if the Pair takes part in the same transaction." That made sense. The Pair is the manager, so its presence was the permission.
The exchange has an action that moves one pile to another trading pool. When you moved pile A, the rules checked carefully that pile A went to the right place. But the rules said nothing about pile B.
That was the hole. Pile B also trusted the Pair. The Pair was already in the transaction. So an attacker could add pile B to the same transaction and send it to their own address. Every check would pass.
Imagine a bank where any drawer opens if the manager has signed the form. The manager signs a form to move drawer A. The thief staples a second request for drawer B to the same signed form. The bank checks drawer A carefully and never looks at drawer B.
The fix is one simple rule. When you move pile A, pile B must not be in the transaction at all. And the other way round.
1if (asset_id == quote_id) {
2 require(!base_id.co_spent());
3} else {
4 require(!quote_id.co_spent());
5}You do not need to read code to understand this. It says: "If we are moving one pile, the other pile must stay out of this transaction."
Sutton also added a test that tries the attack in both directions. With the new rule, the attack fails. Remove either rule, and the matching attack works again.
To be clear, this was a demo. The exchange held no real money and nobody lost anything. Sutton says at the top of the change that the demo "makes no security guarantees", and that a real exchange needs its own design and security review (code change).
The family tree worked. The page check did not
Look at this bug through the idea from the first half.
Pile B recognised the Pair by its covenant ID (code). Nobody could fake that. An attacker could not bring an imitation Pair, because an imitation can never carry the real Pair's ID. In that sense the family tree did its job perfectly.
The problem was inside the real Pair. It was the right manager, signing the right form, but it checked only the drawer it was asked about.
The covenant ID design itself warns about this. KIP-20 says that "to make the mechanism complete", each covenant's own rules must still check that every new output is the expected next step (KIP-20).
So the work is split in two:
- The network guards the family tree. It makes sure every page comes from the real notebook.
- The contract guards each page. It must check everything in the transaction, including the things it was not asked about.
The exchange bug lived entirely in the second half. That is exactly the part that is "a bit tricky to get right".
A second small bug, the same morning
There is a quiet detail here. The new rule uses a "not": the other pile is not in the transaction.
Thirteen minutes before Sutton opened the exchange fix, he merged a change to Argent itself. Argent turns readable code into the low-level script that the network runs. When it translated "this covenant is not in the transaction", it forgot a pair of brackets.
So instead of "not (the number of these coins is more than zero)", the script said "(not the number of these coins) is more than zero". The "not" landed on the wrong thing. A check written to mean "this is not here" could end up testing something else.
The fix added the brackets and tests for many cases. According to Sutton's notes, scripts that were already compiled stay the same (code change).
Two small mistakes, both found and fixed in one morning, both about the word "not". That is a good picture of what careful contract work looks like.
Who should catch mistakes like this?
After the fix, Sutton and IzioDev, another Argent developer, talked about it in the Kaspa R&D Telegram group (Telegram). IzioDev approved the fix and suggested that Argent could force developers to handle this case, a kind of guard against forgetting.
Sutton was not sure.
Then he looked for a middle ground. A whole app could declare which covenants it trusts in this way. Every part of the app would then either check for them on purpose, or Argent would add the "must not be here" rule automatically (Telegram).
IzioDev pointed out a limit. In the bigger exchange example, one action sometimes allows one asset and blocks the other, so a single blanket rule would not fit. He still thought it could help simpler apps, or the compiler could at least add a note (Telegram).
In the end Sutton came back to a different idea. He was "still not convinced" this is the compiler's job (Telegram). When he first started Argent, he wrote, he had in mind an audit tool that inspects contracts and raises flags (Telegram). He described it as "kind of a clippy but for logical/financial and blockchain-specific checks" (Telegram). Clippy is a well-known tool for the Rust language. It reads your code and says "this looks risky, are you sure?" before anything goes wrong.
Both views make sense. One says: make mistakes impossible. The other says: keep the language simple and give builders a second pair of eyes. For now, the answer is the plain one Sutton gave. Complex contracts that hold other people's money need responsibility, auditing and testing.
Why this matters now
Kaspa users already know what happens when a notebook lives in the wrong place.
KRC-20 tokens are a notebook kept outside Kaspa itself. The transfers travel inside normal transactions, but programs called indexers read them and decide who owns what. Sutton reminded people of this in September: "krc20 is not kaspa L1", and its state is interpreted "purely offchain" (X post). In September, an attacker took KRC-20 tokens through a gap in that off-chain reading (our report).
Sutton added in the same post that this is why covenants were built for Kaspa L1, with a token standard called KCC20 on top. With covenants, the notebook lives on Kaspa itself. The network refuses a page that breaks the rules, and the covenant ID stops anyone from starting a fake copy. The KCC20 standard is still a draft (KCC-20).
But the exchange bug is the honest other half of the story. Moving the notebook onto Kaspa makes forgery impossible. It does not make every set of rules complete.
What to keep in mind
- A covenant is only as safe as its rules. The network checks the rules perfectly, but it cannot check whether the rules are the right ones.
- Ask the copy question. If copying something would be harmless, it is a safe. If copying it would be a forgery, it is a notebook, and it needs a real ID and careful page checks.
- Check the ID, not the screenshot. For names and tokens, the covenant ID is what proves you are looking at the real thing.
- "Demo" and "testnet" mean practice. Treat new apps on mainnet with care until they have been reviewed.
- Missing checks are the classic mistake. The exchange bug was not a wrong rule. It was a rule that was never written.
Try it yourself
You can now watch the family tree on the network. Supertypo says the Kaspa.stream explorer lets you pick "Covenant" in the live transactions list. From there you can follow a covenant from its birth, through each new page, to its end (reply). He adds that covenant support there is still early (reply).
Our answer to Sutton's question
Sutton said he was looking for one word that separates the two kinds (reply). By 6 October we had not seen him share his own answer, and we do not know which word he has in mind.
Ours is forgery.
A safe protects coins that the network already knows are real, so a copy of a safe fools nobody. A notebook protects information that only means something if it is the original, so a copy is a forgery. Everything else follows from that: the covenant ID, the lineage, the genesis proofs, and the care every page check needs.
What is your word? Sutton is still reading the answers.
Telegram sources link to public messages in the Kaspa Core R&D (public) Telegram channel.


