OpenPIL
Tiramisu#unavailable

Public interest litigation on Arkiv

A petition, its deadline and the court's answer, on a public record.

OpenPIL records who filed a petition, which registered court holds its review, and whether the court decided before the due date. Anyone can read the same records from the public Tiramisu RPC, without this website.

A demonstration on the Tiramisu testnet. Petitions are synthetic and go to the fictional High Court of Vindhya Pradesh (VPHC). This is not an official filing service.

The problem

Why India

Public Interest Litigation gives civic concerns a route to court. A missing filing record or an unverifiable claim of a decision makes public scrutiny harder for everyone following a case.

  • PIL petitioners

    Need to show that a petition was filed, by whom and when, and to know the date by which the court should respond.

  • Legal aid NGOs

    File and follow petitions on behalf of communities and need a record of each one that does not depend on a single office keeping it.

  • Journalists and civic watchers

    Track which petitions are still pending, which were decided, and which passed their deadline with no decision.

What OpenPIL demonstrates for them

Proof of filing
Each petition records its filing wallet as on chain creator metadata. The petition page shows it next to the court and filing date.
A deadline you can see
The review period is a separate record that expires at the due date. The petition page counts down to it and names the court that holds it.
A decision anyone can check
Accept or reject reasons are stored as records created by the registered court wallet. The Verification panel checks that creator against the registry.
No silent disappearance
When the due date passes without a decision, the petition is shown as Expired, in public, rather than quietly dropping off a list.

Every petition in this demo is synthetic and every decision comes from a demonstration court. OpenPIL shows how such a record could work; it is not connected to any real court, and a record here has no legal effect.

Lifecycle

How it works

A petition and its review are two records with different lifetimes. The review is a lease the court holds until the due date.

One transaction to file

Filing creates two records at once. The petition carries typed attributes and a markdown body. The review carries the due date as its expiry and is handed to the court in the same transaction, so the petitioner stays its creator while the court becomes its owner.

executeBatch2 creates, 1 ownership change, one transaction

petition

title
str
court
strVPHC
filedat
u64
duedate
u64
payload
text/markdown
expires
120 days
$creator
petitioner
$owner
petitioner

review

petition
strkey
court
strwallet
duedate
u64
extensions
i320
payload
application/json
expires
at duedate
$creator
petitioner
$owner
court transferred
Record lifetimesThe petition lives 120 days and Keep alive adds 30 days. The review lives from filing to the due date. An extension moves the due date later. A decision deletes the review; with no decision the review lapses and the petition is Expired.petitionreviewcourt recordscreated with the review, expires after 120 daysKeep alive, +30 dayslease owned by the courtextensiondecisionor lapses at the new due date: Expiredfileddue dateextended due datetime in blocks
  1. 1

    File

    The petitioner signs one transaction that creates the petition and its review, and hands the review to the registered court.

  2. 2

    Under review

    The review record expires at the due date. While it lives, the petition shows Under review with a countdown.

  3. 3

    Extend

    Before the deadline, the court can record a reason and push the due date later. The review's expiry moves with it.

  4. 4

    Decide or expire

    A decision is recorded and the review deleted in one transaction. With no decision by the due date, the status is Expired.

Trust model

Why Arkiv

The point is that readers should not have to trust whoever runs the website. Here is what each alternative would leave to trust.

What the record must proveOperator databaseIPFSOwn contract with a subgraph or APIArkiv
Who filed and who decidedWhatever the operator's table saysContent is addressed by hash; no signer is recordedPossible, if you deploy a contract and someone runs the indexer$creator is set from the signing wallet and cannot be changed
Court holds the reviewA role column the operator can editNo ownership of a fileCustom contract logicOwnership transfer to the court wallet, in the filing transaction
Deadline enforced by the dataA cron job on the operator's serverNo expiry tied to a deadlineCustom contract logic and indexerThe review record expires at its due date
Decision closes the reviewTwo writes the operator promises to do togetherNot applicableCustom contract logicOne batch creates the decision and deletes the live review
Anyone can re-read itOnly through the operator's APIContent, yes. Typed queries, noThrough whoever hosts the subgraph or APITyped queries on the public Tiramisu RPC

What stays off Arkiv

Attributes and payloads are public. OpenPIL keeps these out of every record:

  • Real names and identities of petitioners
  • Sensitive documents and evidence
  • Credentials and private keys
  • Payments

Expiry removes a record from queries but does not erase transaction data or copies others made.

Reproducible without OpenPIL

The app reads through its own keyed proxy at /api/arkiv. The same query works directly against rpc.tiramisu.db-chain.testnet.arkiv.network with no login and no OpenPIL server. Anonymous rate limits still apply.

In the code

How OpenPIL uses Arkiv

Each Arkiv feature and the place you see it in the app. Paths are relative to the repository root.

orgcreated by the registry; names a court wallet and code
petitioncreated by the petitioner
reviewowned by the court
extensioncreated by the court
decisioncreated by the court

Every record has app = "openpil" and a type. Review, extension and decision point at their petition through a petition attribute stored as str. Court trust comes from the registry's org records.

  • Typed attributes and a markdown payload

    Court, title and status filters on Petitions, and the rendered petition body and decision reason.

    title and court are str, filedat and duedate are u64; the body is text/markdown up to 32 KB.

    lib/arkiv/model.tslib/arkiv/writes/markdown.ts

  • Atomic executeBatch filing with predicted keys

    New petition submits once and opens both the petition and its review period.

    predictEntityKeys reserves two keys; one batch creates petition and review and transfers the review to the court. Returned keys are compared with the prediction.

    lib/arkiv/writes/file-petition.ts

  • Expiration as the review deadline

    The Review period ring counts down to the due date and the status becomes Expired if it passes.

    The review is created with ExpirationTime.atDate(duedate). Status is derived from chain block time.

    lib/arkiv/writes/file-petition.tslib/arkiv/trust/revision3.ts

  • Extension batch

    The Due date moves and the petition shows how many times the court extended it.

    One batch creates the reason record, patches review duedate and extensions, and extends the review expiry.

    lib/arkiv/writes/extend-review.ts

  • Decision batch

    Accepted or Rejected with the court's reason; the review period closes.

    One batch creates the decision and deletes the court owned review when it is still live.

    lib/arkiv/writes/decide.ts

  • permissionlessExtension and Keep alive

    Any signed in user with test GLM can keep a petition or decision public for 30 more days.

    extendEntity to the current expiry plus 30 × 43200 blocks. Petitions, decisions, extensions and court records are created with permissionlessExtension.

    lib/arkiv/writes/keep-alive.tslib/arkiv/writes/markdown.tslib/arkiv/writes/entity.ts

  • $creator versus $owner

    The Review period lists the petitioner as creator and the court as owner; a seal marks registered courts.

    A court counts only if its org record was created by the configured registry wallet. Write preflight queries courts with createdBy(registry).

    lib/arkiv/trust/revision3.tslib/arkiv/queries/petitions.ts

  • watchEntityEvents over WebSocket

    Live activity on Petitions refreshes the list when known wallets write.

    Subscribes without fromBlock. Events carry no attributes, so they only trigger a fresh read.

    components/screens/petitions/live-feed.tsxlib/arkiv/clients.ts

  • Cursor pagination

    Counts and status buckets cover every record, not only the first page.

    The shared snapshot follows page.next() until hasNextPage() is false; write preflight follows cursors for courts and related records.

    hooks/use-openpil-snapshot.tslib/arkiv/writes/review-context.ts

Transparency

Verify it yourself

Run this with node --input-type=module from the installed repository. It is the same whole application scan the app's screens are derived from.

Whole application snapshotfrom arkiv/schema.md
import { createPublicClient } from "@arkiv-network/sdk";
import { tiramisu } from "@arkiv-network/sdk/chains";
import { eq, or } from "@arkiv-network/sdk/query";
import { http } from "viem";
const client = createPublicClient({ chain: tiramisu, transport: http() });

let page = await client.select("*")
  .where(eq("app", "openpil")).limit(200).fetch();
const entities = [...page.entities];
while (page.hasNextPage()) {
  page = await page.next();
  entities.push(...page.entities);
}
const timing = await client.getBlockTiming();
console.dir({ entities, timing }, { depth: null });
For one petition, with the scoped helper from the same file:
await scoped(eq("petition", petitionKey.toLowerCase())).fetch();
await client.getEntity(petitionKey);
Network
Arkiv Tiramisu testnet, chain ID 7738577
Data contract
arkiv/schema.md lists every entity type, stored attribute type, expiration and exact query.
Known friction
arkiv/friction.md records SDK and RPC issues met while building, such as typed equality and rate limits.
Demo court

The court page lets anyone accept, reject or extend a review so judges can try the full cycle. The server signs those writes with the demo court key, so every demo decision is created by the registered court wallet regardless of who clicked. In a real deployment only the registered court's own wallet would sign.

Try it

See the record for yourself.

Browse the synthetic petitions, file one with a test wallet, or act as the demo court.