Hodi

Hodi is a consent control plane for creative work used by AI.

A creator registers a work's fingerprint and machine-readable terms without uploading the work itself. A buyer describes the intended use in ordinary language. Gemini 3.5 Flash translates that request into a validated scope, but it never decides permission. A deterministic policy lattice decides whether the creator's terms allow the request.

If consent is withdrawn, a governed fleet of Google ADK agents identifies every affected grant, appends a revocation event, produces notices and receipts, and signs the cascade with Cloud KMS. No agent possesses all of the identity, commercial, evidence, and revocation authority required to act alone.

The model interprets intent. Policy decides permission. Agents execute the process. Evidence proves what happened.

Demo video: Watch the public demo

Live application: Open Hodi

No installation or account is required. The live platform contains:

  • Studio: Register a work, declare permitted uses, and revoke consent.
  • Market: Request a license in ordinary language and receive a deterministic decision.
  • Guided walkthrough: Run the complete five-step demonstration shown in the video.
  • Machine endpoint: Non-browser clients receive a JSON description of the service from the same root URL.

Every quantitative claim in this submission is generated from docs/metrics.json or the live service. make check-docs fails the build if documented figures drift from their producing source.


1. Features and functionality

The problem

A creator's voice, writing, code, or music can appear inside an AI product without any practical mechanism through which the creator could have agreed, refused, set a price, limited the use, or later withdrawn permission.

Hodi is what you call at someone's door before entering.

Hodi is the knock.

Hodi provides the missing consent layer: a governed system in which creators declare terms, buyers request specific uses, deterministic policy decides whether those terms permit the request, and agents administer the resulting evidence.

1. Register without surrendering the work

A creator registers:

  • Title and medium
  • Canonical URI
  • File size
  • Browser-computed SHA-256 fingerprint
  • Control tier
  • Machine-readable consent terms

The creative file never leaves the browser. Hodi stores a claim about a work, not the work itself.

Every registration must declare one of three control tiers:

  • verified_control: control is supported by a stored, resolving proof
  • asserted: the registrant asserts control, but Hodi has not independently verified it
  • disputed: the control claim is contested

The schema makes verified_control without a proof unconstructible. make verify-manifest retrieves the live manifest and fails if a verification URI stops resolving.

The current live manifest contains five registrations drawn from the author's own published writing, repositories, bass recordings, and stems. They are presently labeled asserted, not verified_control. Two demonstration registrations have canonical URIs that do not currently resolve. Hodi exposes those facts rather than silently upgrading the claims.

This tiering has already caught a real defect. Three registrations previously claimed verified_control using proofs that did not establish control. They were downgraded, the verifier was corrected, and the audit became a recurring automated check.

2. Express consent as structured policy

Hodi represents a permitted use as a five-dimensional Scope:

  1. Use type
  2. Model class
  3. Commercial status
  4. Territory
  5. Temporal validity

Use types form a declared partial order:

training
└── fine_tuning
    └── rag_retrieval
        └── human_reference
synthesis is incomparable

This hierarchy is stored as data rather than duplicated through branching application logic.

The deterministic permits() predicate evaluates all five dimensions within one grant. It never combines dimensions from separate grants. For example, a fine-tuning grant and an unrelated commercial grant cannot accidentally compose into permission for commercial fine-tuning.

A 56-case truth table verifies the scope model in CI through:

make verify-scopes

3. Ask for a license in ordinary language

A buyer can submit a request such as:

We would like to fine-tune an open-weights model on this work for non-commercial research in the United States and Canada.

POST /api/v1/license/natural sends the sentence to Gemini 3.5 Flash on Vertex AI. Gemini converts the request into a typed Scope.

Gemini does not grant or refuse permission.

Its output must pass strict schema validation. Unknown use types, malformed values, missing fields, and extra fields receive HTTP 422. A security test injects an interpretation containing:

{"permitted": true}

The entire interpretation is rejected. The unauthorized field is not merely removed.

The model interprets intent. The lattice decides permission.

This preserves the usability of natural language without placing the consent decision inside a probabilistic model.

4. Separate agents by conflicting authority

Hodi uses four institutional agents:

Agent Responsibility Information it must not possess
Rights Custodian Work ownership and creator identity Buyer negotiation history
Licensing Negotiator One buyer's request and eligible terms Other counterparties' terms
Evidence Agent Independent access and provenance evidence Commercial negotiation terms
Revocation Propagator Withdrawal and downstream termination Creator identity beyond required authorization

The agents are separated by conflict of interest rather than by arbitrary task boundaries.

No service account holds two of the following authority classes:

  • Identity
  • Buyer terms
  • Evidence
  • Revocation

Every inter-agent call crosses a policy gateway. A denied request becomes a structured PolicyDenialEvent with the calling identity, consulted policy, requested collection, and outcome. Denials are never represented only as stack traces or silent refusals.

A monolithic agent would undermine Hodi's central guarantee because the same process would be able to see identity, commercial terms, evidence, and revocation authority simultaneously.

5. Revoke consent with a signed, append-only cascade

When a creator revokes use type R, Hodi terminates every active grant whose scope permits R.

The revocation path uses the same deterministic permits() predicate as licensing. A grant is affected precisely when it could have exercised the withdrawn use.

The propagator:

  1. Authenticates the creator.
  2. Confirms that the creator owns the work.
  3. Traverses the declared scope lattice.
  4. Identifies every affected active grant.
  5. Appends a revocation event.
  6. Produces notices and receipts.
  7. Signs the resulting artifacts with Cloud KMS ECDSA P-256.
  8. Closes the standing offer derived from the event log.

The original grant is never deleted or rewritten. It remains visible and is displayed as superseded.

Revocation is a new event, not a mutation of history.

The live interface also demonstrates the practical consequence: after a creator revokes a use, a request that had been permitted moments earlier is refused with the reason stated.

One scope-model limitation remains explicit. If a grant sits above the revoked use in the single-valued use hierarchy, Hodi terminates the grant as a whole. The current representation cannot express "training except fine-tuning." This is the conservative direction because Hodi terminates only grants that genuinely permitted the revoked use.

6. Recover from a failed or looping agent

The guided demonstration can force the revocation propagator into an infinite loop.

When the worker exceeds its one-second deadline:

  1. The Supervisor records TaskAbandoned.
  2. The Registry quarantines the unhealthy worker for the remainder of the run.
  3. The task is routed to a standby.
  4. The standby returns an explicitly partial result.
  5. No notices or grant events are appended because the failed worker's write state cannot be trusted.

The deterministic affected set is still returned, but Hodi does not pretend that downstream actions completed.

Quarantine and rerouting appear as spans within the same distributed trace. The measured deployed path averaged approximately 1.11 seconds against the configured one-second worker deadline.

7. Provide a safe public demonstration

The public Studio and Market use a dedicated sandbox_agent role.

Its gateway policy names every real collection under denied_collections. A demonstration request aimed at real data is rejected by the same policy gateway used by the institutional agents, not by a route-specific string check.

The public natural-language endpoint is bounded through:

  • Maximum input length
  • Per-session request cap
  • Per-IP rate limiting

Registration stores only the work claim and browser-computed fingerprint. It does not provide an unauthenticated content-upload surface.

The live demonstration uses the deployed application and actual policy engine, ADK execution, Vertex AI interpretation, Firestore event log, Cloud KMS signatures, and denial gateway. Demonstration data remains isolated from real collections.

8. Prove the system from a clean clone

The repository includes an offline verification path:

make demo

The command exercises the complete system spine from a clean container with networking disabled. Each step asserts the property it demonstrates instead of merely printing a successful-looking transcript.

The online demonstration proves the deployed Google Cloud path. The offline demonstration makes the core policy and authority properties reproducible without credentials.


2. Technologies used

Gemini 3.5 Flash through Vertex AI

Hodi uses the pinned model ID:

gemini-3.5-flash

It runs through the Vertex AI global endpoint with temperature zero.

Gemini performs two bounded tasks:

  • Translate natural-language licensing requests into typed scopes
  • Draft revocation notices that must pass deterministic linting

Gemini cannot grant permission, modify the consent hierarchy, issue a license, identify affected grants, or append a revocation event.

The deployed model was selected after probing available model IDs and regions. gemini-3.5-flash is the pinned stable 3.5-generation model used for recorded timings and verification.

Google Agent Development Kit

Hodi uses:

google-adk==2.6.2

The agents are real google.adk.agents.BaseAgent subclasses executed by a real google.adk.runners.Runner.

They extend BaseAgent rather than LlmAgent deliberately. Each hop in the fleet represents an authority or policy decision. Inserting a language model into those hops would contradict the architecture's purpose.

Gemma on Vertex AI

gemma-4-26b-a4b-it-maas performs first-pass triage of crawler access records before selected records reach Gemini.

This component is intentionally non-load-bearing. If Gemma is unavailable, Hodi falls back to Ollama and then to a deterministic heuristic. Evidence records are still produced and labeled with the method used.

Firestore

Firestore stores the append-only grant-event log.

A custom IAM role, hodiAppendOnlyGrantWriter, grants:

  • create
  • get
  • list

It withholds:

  • update
  • delete

The policy applies to the agent service accounts and the identity used by the deployed Cloud Run revision. Gateway writes use .create(), which fails on an existing event ID. They never use .set(), which would permit upsert behavior.

Event IDs are deterministic. Completed history therefore cannot be silently overwritten through either the application path or the deployed runtime identity.

Identity, commercial, evidence, and adjudication data are separated into named Firestore databases behind private Cloud Run workloads and IAM-conditioned service accounts. The shared append-only grant log remains in the default database, with counterparty row separation enforced by the gateway.

Cloud KMS

Cloud KMS signs revocation artifacts using ECDSA P-256.

The public demonstration returns the signature and published public key so the browser can verify the artifact independently.

Historical unsigned or superseded signature vintages remain visible because Hodi does not rewrite its event history.

OpenTelemetry and Cloud Trace

Every agent decision emits a span containing:

  • agent.identity
  • policy.consulted
  • outcome

Application spans are nested within ADK's own invoke_agent spans. A delegation across multiple agents and service accounts appears as one trace while preserving each institutional boundary.

Cloud Run

Cloud Run hosts:

  • The evidence endpoint
  • The Studio and Market
  • The licensing API
  • Private agent workloads
  • The nightly teardown job

The public service is configured with:

min-instances=0
max-instances=5

Cloud Scheduler

Two deployed jobs have recorded execution history:

  • Daily accrual audit at 09:00 UTC
  • Nightly teardown at 23:00 UTC

Scheduler calls are authenticated using OIDC.

Cloud Logging

Gateway denials are written as structured jsonPayload records. They can be queried by:

  • Calling service account
  • Requested collection
  • Consulted policy
  • Outcome

Antigravity evaluation

The project also evaluated whether the Antigravity SDK could execute a headless, server-side, multi-agent delegation under distinct service accounts while emitting one OpenTelemetry span per decision.

The pre-committed harness failed immediately with:

No module named 'google.antigravity'

No usable headless Python surface was available for this requirement, so the project switched to ADK on the same day. The harness, failure condition, and decision are published as a negative result rather than presented as an Antigravity integration.


3. Other data sources

Creator-owned works

The live manifest contains five registrations based on the author's own output:

  • Medium essays
  • Public GitHub repositories
  • Original electric bass recordings
  • Original stems

Hodi did not purchase or scrape a creative corpus. The creative files are not stored by the registry.

All five current registrations are explicitly labeled asserted. No registration currently claims verified_control.

First-party access records

A Cloud Run evidence endpoint has recorded access activity since August 6, 2026.

Each record contains:

  • Timestamp
  • Requested path
  • User agent
  • Source address
  • Referrer
  • Whether robots.txt was fetched first

The service publishes:

  • robots.txt
  • /.well-known/hodi.json

The robots.txt file points directly to the machine-readable consent document.

As of the August 29, 2026 audit:

  • 6,956 total accrued records
  • 6,675 generated by the project's own instrumented tooling
  • 281 non-self-originated but browser-like records labeled unattributed
  • 29 records matching a generic crawler user-agent signature
  • 3 distinct crawler-signature user agents
  • 21 crawler-signature visits to /robots.txt
  • 1 visit to /.well-known/hodi.json

The central observation is narrow:

Of 29 requests carrying crawler-identifying user agents, 21 fetched robots.txt, but only one followed the machine-readable consent link named inside it.

These records are attributable, not authenticated. A user agent is self-declared, and a source address does not establish which organization controlled the request.

The detector matches generic self-identification such as bot, crawler, spider, or scraper. It does not enumerate companies, and the submission does not name any real company as a violator.

The figures can be regenerated through:

make metrics
make check-docs

Canary strings

Canary strings were added to newly published items beginning August 6, 2026 at 12:40 UTC.

They apply only to work published after that timestamp. No retroactive coverage is claimed.

No third-party accusations

No third-party dataset was purchased or incorporated.

Every adversarial organization in the fixtures is fictional, including:

acme-intelligence-labs
rival-labs
buyer-acme-*

Real crawler user-agent strings appear only as classification inputs in tests and recorded model-response caches. They are not treated as proof that a named organization used a creative work.


4. Findings and learnings

Hodi's central principle is that a system should refuse to assert what it cannot verify.

During development, the project recorded every place where its own implementation violated that principle.

The resulting defect ledger contains:

  • 66 defects
  • 9 defect classes
  • 4 classes that recurred after an initial fix

The full ledger is published in docs/defect_ledger.json.

Finding 1: Authentication and filtering cannot share the same untrusted value

An early licensing endpoint accepted counterparty_id from the request body.

That value was used both:

  • As the database query filter
  • As the session identity against which the filter was validated

An unauthenticated caller could therefore supply another counterparty's identifier and receive that party's grant and receipt.

A related defect later appeared in the revocation route, which initially lacked authentication entirely. Because revocations enter an append-only log, those unauthorized writes could not simply be erased.

The lasting correction was structural:

  • Counterparty identity is derived from a verified credential.
  • The request body cannot select the authenticated identity.
  • The original exploit is a permanent regression test.
  • A CI guard enumerates mutating routes and fails if one lacks authentication.

The lesson is broader than this endpoint:

Comparing two attacker-controlled values does not create an authorization boundary.

Finding 2: Observability infrastructure can contaminate its own evidence

The project initially counted some of its own scheduled requests as third-party crawler activity because the self-traffic classifier omitted the Cloud Scheduler user agent.

That false positive could have changed the project's empirical conclusion.

A separate detector bug also created false negatives because the crawler pattern required a word boundary before bot. Vendor prefixes joined directly to bot did not match.

After both corrections:

  • Project-generated traffic is classified separately.
  • Browser-like traffic is labeled unattributed.
  • Only generic crawler-signature matches are called crawler activity.
  • Every published number is regenerated from source.
  • Documentation drift fails the build.

The lesson:

Evidence pipelines require the same adversarial testing as the systems they evaluate.

Finding 3: Documentation generated from source can still preserve a bad claim

The README originally claimed that the overclaim detector caught paraphrased assertions.

A 12-case paraphrase probe showed that the deterministic regex layer rejected only 4 of 12 examples.

The claim was replaced with the measured result.

A Gemini embedding backstop now raises measured coverage to 12 of 12. It is permitted only to add a refusal. If the embedding model is unavailable, the system falls back to the deterministic 4-of-12 layer rather than silently allowing more claims.

Both measurements remain published because one depends on a model and the other does not.

The schema remains the actual invariant.

The lesson:

Generating documentation from source prevents drift. It does not prove that the source itself is correct.

Additional ADK learning

ADK supports deterministic multi-agent systems without placing an LLM in every agent.

A governed agent can subclass BaseAgent and implement _run_async_impl, allowing ADK to provide orchestration and tracing while deterministic code retains authority.

The project also discovered a subtle state-management issue: BaseAgent is a Pydantic model. Shared mutable state supplied as a model field can be deep-copied per agent. Each agent may mutate its own copy while the orchestrator observes no changes, producing an apparently successful run with an empty result.

That behavior is now covered by regression tests and documented for other ADK builders.

Designed limits

Hodi intentionally does not claim that:

  • It can determine whether a work was included in a frontier model's training data.
  • Revocation can un-train an existing model.
  • A crawler user agent proves the identity of the organization making a request.
  • A source address proves organizational control.
  • A checked verbatim_match demonstrates misuse by a third party.
  • Local prompt inspection is Google Model Armor.

Training-set membership is not represented in the evidence schema. Every evidence record carries an explicit claim_limit.

Revocation terminates a grant and closes future standing offers. It does not remove information from an already trained model.

The managed Model Armor integration was unavailable because template creation returned HTTP 403 under its restricted preview. The claim was removed. The remaining prompt inspection is labeled local_regex_inspector, and the security model rests on IAM boundaries, deterministic policy, gateway enforcement, and inspectable traces.


Production evidence and project links

Live surfaces

Build and audit evidence

Social posts use #AllThingsAgenticHackathon.

Built With

  • cloud-kms
  • cloud-run
  • cloud-scheduler
  • cloud-trace
  • firestore
  • gemini
  • gemma
  • google-adk
  • opentelemetry
  • python
  • vertex-ai
Share this project:

Updates

Submission history