Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pnpm + turborepo monorepo. Node 24, pnpm only (`preinstall` blocks npm/yarn).

- `packages/contracts` — Solidity contracts (Foundry), deploy scripts, wagmi-generated TS bindings in `generated/`. Exports `@hollab-io/contracts/actions`.
- `packages/dao-contracts` — DAO layer contract extensions (ERC20Votes governor, timelock, treasury).
- `packages/hollab-sdk` (`@hollab-io/hollab-sdk`) — Private data layer SDK: key management, encrypted 0G storage, event indexer. `tsc` build.
- `packages/hollab-sdk` (`@hollab-io/hollab-sdk`) — Private data layer SDK: key management, encrypted storage, event indexer. `tsc` build.
- `packages/agent-sdk` (`@hollab-io/agent-sdk`) — SDK for agents to integrate with hollab.eth governance. `tsup` build. Depends on `contracts` + `indexing-client`.
- `packages/indexing-client` — Typed client for the Ponder indexer. `tsup` build.
- `packages/viem-extension` — Viem client extensions.
Expand Down Expand Up @@ -39,7 +39,7 @@ forge build
forge test -vvv
forge coverage
pnpm generate # wagmi generate → generated/ TS bindings
pnpm deploy:infra:sepolia # DeployInfrastructure.s.sol (also :mainnet, :0g-testnet, :0g-mainnet, :local)
pnpm deploy:infra:sepolia # DeployInfrastructure.s.sol (also :mainnet, :local)
pnpm deploy:org:sepolia # CreateOrganization.s.sol
```

Expand Down Expand Up @@ -109,9 +109,11 @@ on-chain events
└─ agent-sdk
```

Frontend reads go through `useQuery` hooks wrapping the indexing-client; writes go through `useMutation` wrapping wagmi/viem calls. Multi-chain config (Sepolia default, Mainnet, 0G) lives in `apps/hola-modern/src/config/chains.ts`.
Frontend reads go through `useQuery` hooks wrapping the indexing-client; writes go through `useMutation` wrapping wagmi/viem calls. Multi-chain config (Sepolia default, Mainnet coming soon) lives in `apps/hola-modern/src/config/chains.ts`.

**Private data layer** (`packages/hollab-sdk`): ContentRef hashes on-chain point to encrypted blobs stored on 0G. SDK handles key management and encrypted storage so proposal content / meeting transcripts stay off-chain but verifiably committed.
**Private data layer** (`packages/hollab-sdk`): ContentRef hashes on-chain point to encrypted blobs stored on IPFS. SDK handles key management and encrypted storage so proposal content / meeting transcripts stay off-chain but verifiably committed.

**IPFS pin proxy** (`apps/hollab-indexing`): the indexer's Hono API exposes `POST /storage/pin` and (in local mode) `GET /ipfs/:cid`. When `PINATA_JWT` is set the proxy forwards uploads to Pinata; otherwise it falls back to a local filesystem store under `LOCAL_IPFS_DIR` (default `.ponder/local-ipfs`). Both branches return CIDv0; the frontend extracts the 32-byte sha2-256 multihash digest as `bytes32` for on-chain `ContentRef.contentHash`. Reads in production hit `VITE_IPFS_GATEWAY/ipfs/<cid>`; in local dev, point that env at the indexer URL.

## Conventions

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ This is the full loop: **create** an org, **structure** it through governance, *
## Status (What Works Today)

- **Proposal lifecycle end-to-end on-chain** — `createProposal` → `raiseObjection` / `resolveObjection` → `adopt` / `discard`, indexed and queryable. `executeGovernance` has been removed; the lifecycle primitives are the only path.
- **Readable tensions on-chain** — `createProposalWithTension(...)` publishes plaintext via a `ProposalTensionPublished` event; the on-chain `tensionHash` is derived (`keccak256`) so the content address stays canonical. Long or sensitive content can still commit only a hash and resolve via IPFS / 0G / encrypted backends.
- **Readable tensions on-chain** — `createProposalWithTension(...)` publishes plaintext via a `ProposalTensionPublished` event; the on-chain `tensionHash` is derived (`keccak256`) so the content address stays canonical. Long or sensitive content can still commit only a hash and resolve via IPFS-pinned encrypted blobs.
- **Agent-native governance** — attribution-only proposer model (propose as a member or as a role you lead), per-org `MAX_PROPOSAL_AGE` (default 7 days, bounds [1 hour, 30 days]), and async proposal queues instead of mandatory synchronous meetings. See [`specs/99`](./specs/99-agent-native-divergence.md).
- **ERC-8004 agent identity** — members can bind an agent NFT to their org identity via `OrganizationInstance.linkAgentIdentity(...)`; ownership is verified on-chain, enabling an agent-native surface on top of the same governance primitives.
- **Officer bootstrap + election lock** — admins can set a circle's Facilitator / Secretary to any address (human or autonomous agent) pre-election; `isFacilitatorElected` / `isSecretaryElected` flip once a corresponding election adopts, after which the bootstrap setters are locked and governance owns the role.
Expand Down Expand Up @@ -93,7 +93,7 @@ The design principle is simple: **on-chain for commitments, off-chain for coordi

Meeting coordination events (IDM steps, agenda items, nominations) are emitted as **events only** — the indexer reconstructs the full meeting state, but the chain only stores what matters: who ended up in which role, and which governance changes were adopted.

Proposals may include a short plaintext tension inline — published via the `ProposalTensionPublished` event log — or a **ContentRef** hash pointing to off-chain encrypted content (IPFS / 0G). The ledger proves _that_ a proposal with specific content was adopted: short tensions stay readable without any off-chain retrieval, longer or private content stays addressable but off-chain.
Proposals may include a short plaintext tension inline — published via the `ProposalTensionPublished` event log — or a **ContentRef** hash pointing to off-chain encrypted content (IPFS). The ledger proves _that_ a proposal with specific content was adopted: short tensions stay readable without any off-chain retrieval, longer or private content stays addressable but off-chain.

## Architecture

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/docs/pages/contracts/meeting-factory.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Any org member can create a proposal or raise an objection. Only org admins can
| `circleId` | Circle the change targets (anchor = 0) |
| `proposer` | `msg.sender` at create time |
| `proposerRoleId` | 0 if the proposer is not currently a role lead |
| `tensionHash` | `bytes32` — CIDv1 / 0G root / keccak256 of off-chain tension text |
| `tensionHash` | `bytes32` — sha2-256 digest of an IPFS CIDv0, or keccak256 inline |
| `changeType` | `HolacracyTypes.ChangeType` (CreateRole, AmendRole, Election, …) |
| `changeData` | ABI-encoded payload, decoded by `_applyChange` on adopt |
| `status` | `Draft` → `Adopted` or `Discarded` (MVP uses these three) |
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/docs/pages/contracts/role-registry.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function createRoleWithRefs(
) external returns (uint256 roleId)
```

This supports the hybrid on-chain/off-chain model — role names and structure live on-chain, while detailed descriptions can be stored encrypted on 0G Network or IPFS.
This supports the hybrid on-chain/off-chain model — role names and structure live on-chain, while detailed descriptions can be stored encrypted on IPFS (pinned via the indexer pin-proxy).

## Circle Tracking

Expand Down
30 changes: 15 additions & 15 deletions apps/docs/docs/pages/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ hollab.eth replaces top-down hierarchy with a structured system of **roles**, **

The system separates **onchain commitments** from **offchain coordination**:

| Onchain | Offchain |
| --------------------------------------- | --------------------------------------- |
| Organization structure (roles, teams) | Sync facilitation |
| Governance outcomes (adopted proposals) | Proposal content & discussions |
| Membership & voting power | Encrypted private data (via 0G Network) |
| Token balances & delegation | Calendar scheduling |
| Onchain | Offchain |
| --------------------------------------- | --------------------------------- |
| Organization structure (roles, teams) | Sync facilitation |
| Governance outcomes (adopted proposals) | Proposal content & discussions |
| Membership & voting power | Encrypted private data (via IPFS) |
| Token balances & delegation | Calendar scheduling |

Smart contracts enforce the rules — only proposal reviews can modify org structure, voting follows token-weighted power, and membership is tracked transparently.

Expand All @@ -34,15 +34,15 @@ hollab.eth is designed for both humans and AI agents. The agent SDK allows progr

## Stack

| Layer | Technology |
| ---------- | ------------------------------------------- |
| Contracts | Solidity, Foundry, OpenZeppelin |
| Indexer | Ponder (event indexing → GraphQL) |
| Frontend | React 19, Vite, Wagmi, RainbowKit |
| SDK | TypeScript, HKDF key derivation, 0G Network |
| Agent SDK | TypeScript, viem, framework-agnostic |
| Identity | ENS subdomains (`myorg.hollab.eth`) |
| Deployment | IPFS via omnipin, ENS contenthash |
| Layer | Technology |
| ---------- | ------------------------------------- |
| Contracts | Solidity, Foundry, OpenZeppelin |
| Indexer | Ponder (event indexing → GraphQL) |
| Frontend | React 19, Vite, Wagmi, RainbowKit |
| SDK | TypeScript, HKDF key derivation, IPFS |
| Agent SDK | TypeScript, viem, framework-agnostic |
| Identity | ENS subdomains (`myorg.hollab.eth`) |
| Deployment | IPFS via omnipin, ENS contenthash |

## Chains

Expand Down
15 changes: 8 additions & 7 deletions apps/docs/docs/pages/operations/docker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,18 @@ Concurrency groups cancel in-progress runs on the same branch.

### IPFS Deployment

On push to `release` branch (or manual trigger), `deploy-ipfs.yml`:
Frontend deploys are **not** run in CI — they go through a local script, [`scripts/deploy-ipfs.sh`](https://github.com/hollab-io/hollab-cannes-26/blob/dev/scripts/deploy-ipfs.sh), so the deployer key never lives in CI and every production push is multisig-approved. The script:

1. Builds the frontend with production env vars
2. Deploys to IPFS via **omnipin** (Filecoin pinning)
3. Updates the `hollab.eth` ENS **contenthash** through a Safe multisig transaction
1. Builds the frontend with production env vars (`pnpm --filter hola-modern build`)
2. Pins `dist/` to IPFS via **omnipin** (Filecoin pinning)
3. Emits the new CID plus the encoded `setContenthash` calldata for a human-signed **Safe** transaction that updates the `hollab.eth` ENS **contenthash**

Required secrets:
Required env (local shell, not CI secrets):

- `OMNIPIN_FILECOIN_TOKEN` — Filecoin pinning API token
- `OMNIPIN_PK` — Deployer private key
- `SAFE_ADDRESS` — Safe multisig address for ENS updates
- production `VITE_*` build vars (gateway, indexer URLs, WalletConnect project id) — typically in `apps/hola-modern/.env.production`

See [IPFS Deployment](/operations/ipfs) for the full flow.

### Shared Setup

Expand Down
20 changes: 13 additions & 7 deletions apps/docs/docs/pages/operations/ipfs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,20 @@ The frontend uses hash-based routing (`#/org/:id/tactical`) instead of path-base
- There is no server to handle path-based routing fallbacks
- Hash fragments (`#/...`) are handled entirely client-side

## Manual Deployment
## Deployment

```bash
# Build the frontend
pnpm --filter hola-modern build
Deployment is driven by a single local script, [`scripts/deploy-ipfs.sh`](https://github.com/hollab-io/hollab-cannes-26/blob/dev/scripts/deploy-ipfs.sh):

# The dist/ folder is ready for IPFS upload
ls apps/hola-modern/dist/
```bash
# Pin token in env; production VITE_* vars in apps/hola-modern/.env.production
OMNIPIN_FILECOIN_TOKEN=… ./scripts/deploy-ipfs.sh
```

The actual IPFS upload and ENS update are handled by the `deploy-ipfs.yml` GitHub Action, which uses omnipin CLI tools and Safe SDK for the multisig transaction.
The script:

1. **Preflights** the required env (pin token, production `VITE_*` vars) and the `omnipin` CLI
2. **Builds** the frontend (`pnpm --filter hola-modern build`, including the `dist/index.html` → `dist/404.html` copy)
3. **Pins** `apps/hola-modern/dist/` to IPFS/Filecoin via omnipin and captures the CID
4. **Emits** the resulting CID, a gateway preview URL, and the encoded `setContenthash` calldata to paste into the **Safe Transaction Builder**

The script never submits the multisig transaction — the `hollab.eth` contenthash update is a human-signed Safe step (see [ENS Contenthash](#ens-contenthash) above). This keeps the deployer key out of CI and ensures every production push is multisig-approved.
2 changes: 1 addition & 1 deletion apps/docs/docs/pages/sdk/agent-sdk.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ import { ChangeType, encodeCreateRole } from "@hollab-io/agent-sdk";

// 1. Any member creates a Draft proposal. tensionText is hashed client-side
// with keccak256 — only the hash is committed on-chain. Pass tensionHash
// directly if the full text is already pinned to IPFS / 0G.
// directly if the full text is already pinned to IPFS.
const { proposalId } = await agent.governance.createProposal(meetingFactoryAddr, {
orgId,
circleId: 1n,
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/docs/pages/sdk/key-management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ This maps directly to the `ContentRef` struct used in RoleRegistry for on-chain

## Stream IDs

Data is addressed on the 0G Network using stream IDs built from org/circle/role identifiers:
Data is addressed by content hash on IPFS; logical stream IDs built from org/circle/role identifiers name each blob and resolve to its latest CID:

```ts
import { buildCircleMetaKey, buildOrgMetaKey, buildStreamId } from "@hollab-io/hollab-sdk";
Expand Down
13 changes: 10 additions & 3 deletions apps/docs/docs/pages/sdk/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The `hollab-sdk` package (`packages/hollab-sdk/`) provides cryptographic key man

## Purpose

While smart contracts handle on-chain commitments (org structure, votes, membership), some governance data is better kept private — internal issues, draft proposals, role descriptions, and meeting notes. The SDK provides end-to-end encryption for this data using the 0G Network for storage.
While smart contracts handle on-chain commitments (org structure, votes, membership), some governance data is better kept private — internal issues, draft proposals, role descriptions, and meeting notes. The SDK provides end-to-end encryption for this data using IPFS for storage (encrypted blobs pinned via the indexer pin-proxy).

For programmatic access by AI agents and automated systems, see the [Agent SDK](/sdk/agent-sdk).

Expand All @@ -31,14 +31,21 @@ See [Key Management](/sdk/key-management) for details.

### StorageClient

Interacts with 0G Network for encrypted blob storage:
Interacts with IPFS (via the indexer pin-proxy) for encrypted blob storage:

```ts
import { StorageClient } from "@hollab-io/hollab-sdk";

const storage = new StorageClient(config);
const storage = new StorageClient(keyManager, {
pinUrl: "http://localhost:42069/storage/pin",
gatewayUrl: "http://localhost:42069",
});
```

Writes encrypt and pin a blob, returning its bytes32 `contentHash`; reads take
that hash, fetch from the gateway, and decrypt. The logical-key → `contentHash`
mapping lives on-chain (`ContentRef`).

### OrgClient

High-level client for reading and writing organization data:
Expand Down
Loading
Loading