Skip to content

Sovereign V2 Genesis Push#13

Open
Freedomwithin wants to merge 1 commit into
fairscale-integrationfrom
trustchain-sovereign-v2-15335853524727996490
Open

Sovereign V2 Genesis Push#13
Freedomwithin wants to merge 1 commit into
fairscale-integrationfrom
trustchain-sovereign-v2-15335853524727996490

Conversation

@Freedomwithin

@Freedomwithin Freedomwithin commented Feb 14, 2026

Copy link
Copy Markdown
Owner
  • Merged feature/fairscale-production-bounty and feature/on-chain-notary.
  • Resolved conflict in backend/package.json.
  • Updated backend/services/notary_sync.ts to use "config" seed.
  • Updated on-chain/trustchain-notary/programs/trustchain-notary/src/lib.rs to add GlobalConfig struct and use "config" seed for UserIntegrity.
  • Verified backend functionality.

PR created automatically by Jules for task 15335853524727996490 started by @Freedomwithin

Summary by Sourcery

Integrate an on-chain notary program with backend and frontend integrity checks, adding support for syncing verified integrity scores to Solana and gating protected actions in the UI based on wallet integrity.

New Features:

  • Add a Solana notary sync script in the backend to update on-chain user integrity PDAs using a configured notary keypair and program.
  • Introduce a new Anchor-based on-chain trustchain-notary program to store and update per-user integrity scores under a config-seeded PDA.
  • Add a frontend integrity hook and Guard component to fetch integrity verification from the backend and block high-risk wallets from protected actions based on Gini score and status.

Build:

  • Extend backend package configuration with a notary sync script and new Solana/crypto dependencies, and add Anchor/Cargo configuration for the on-chain notary program.

Documentation:

  • Add reconstructed FairScale API documentation explaining reputation endpoints and how they integrate with TrustChain's integrity and gating logic.

… with conflict resolution

Co-authored-by: Freedomwithin <166790647+Freedomwithin@users.noreply.github.com>
@vercel

vercel Bot commented Feb 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
trust-chain-backend Ready Ready Preview, Comment Feb 14, 2026 8:45pm
trust-chain-frontend Ready Ready Preview, Comment Feb 14, 2026 8:45pm

@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@sourcery-ai

sourcery-ai Bot commented Feb 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements the first version of the TrustChain notary on-chain program plus a backend notary sync script, and wires them into the existing integrity engine and frontend via a reusable hook and guard component, while standardizing the PDA seed to "config" for integrity data storage.

Sequence diagram for frontend integrity verification flow

sequenceDiagram
    actor User
    participant FrontendGuard as Frontend_Guard
    participant UseIntegrityHook as UseIntegrity_Hook
    participant WalletAdapter as Wallet_Adapter
    participant BackendAPI as Backend_API

    User->>FrontendGuard: Navigate to protected feature
    FrontendGuard->>UseIntegrityHook: useIntegrity()
    UseIntegrityHook->>WalletAdapter: Read publicKey, connected
    alt Wallet connected
        UseIntegrityHook->>BackendAPI: POST /api/verify { address }
        BackendAPI-->>UseIntegrityHook: { giniScore, hhiScore, status }
        UseIntegrityHook-->>FrontendGuard: { giniScore, status, loading=false, error=null }
        FrontendGuard->>FrontendGuard: Compute isSybil based on threshold and status
        alt isSybil
            FrontendGuard-->>User: Show blocked fallback or default guard-blocked UI
        else not Sybil
            FrontendGuard-->>User: Render protected children content
        end
    else Wallet not connected
        UseIntegrityHook-->>FrontendGuard: { giniScore=null, status=null, loading=false }
        FrontendGuard-->>User: Prompt to connect wallet or show neutral UI
    end
Loading

Sequence diagram for backend notary sync to on-chain program

sequenceDiagram
    participant NotarySyncScript as Notary_Sync_Script
    participant Env as Environment
    participant IntegrityEngine as Integrity_Engine
    participant SolanaRPC as Solana_RPC
    participant TrustchainProgram as Trustchain_Notary_Program

    NotarySyncScript->>Env: Load NOTARY_SECRET
    Env-->>NotarySyncScript: Secret key bytes
    NotarySyncScript->>NotarySyncScript: Derive NOTARY_KEYPAIR

    NotarySyncScript->>IntegrityEngine: fetchVerifiedScore(MOCK_WALLET)
    IntegrityEngine-->>NotarySyncScript: IntegrityScore { gini, hhi, status }

    NotarySyncScript->>SolanaRPC: getLatestBlockhash()
    SolanaRPC-->>NotarySyncScript: blockhash

    NotarySyncScript->>NotarySyncScript: Derive PDA with seeds ["config", wallet]
    NotarySyncScript->>NotarySyncScript: Build instruction data (discriminator + gini + hhi + status)
    NotarySyncScript->>NotarySyncScript: Create Transaction with accounts [user_integrity PDA, user, notary, system_program]
    NotarySyncScript->>NotarySyncScript: Sign transaction with NOTARY_KEYPAIR

    NotarySyncScript->>NotarySyncScript: Verify signatures via transaction.verifySignatures()
    NotarySyncScript->>NotarySyncScript: Verify Ed25519 with nacl.sign.detached.verify
    alt Signature valid
        NotarySyncScript-->>TrustchainProgram: Submit signed transaction update_integrity
        TrustchainProgram->>TrustchainProgram: Check notary signer matches NOTARY_PUBKEY
        TrustchainProgram->>TrustchainProgram: Update UserIntegrity account at PDA
        TrustchainProgram-->>NotarySyncScript: Success
    else Invalid signature
        NotarySyncScript-->>NotarySyncScript: Throw Security Violation error
    end
Loading

Class diagram for new frontend integrity hook and guard component

classDiagram
    class IntegrityData {
        +number giniScore
        +number hhiScore
        +string status
        +boolean loading
        +string error
    }

    class UseIntegrityHookModule {
        +API_BASE_URL : string
        +useIntegrity() IntegrityData
    }

    class GuardProps {
        +ReactNode children
        +ReactNode fallback
        +number threshold
        +string sybilStatus
    }

    class GuardComponent {
        +Guard(props GuardProps) ReactNode
    }

    class IntegrityConstants {
        +number PROBATIONARY_THRESHOLD
        +string SYBIL_STATUS
    }

    UseIntegrityHookModule --> IntegrityData : returns
    GuardComponent --> GuardProps : uses
    GuardComponent --> UseIntegrityHookModule : calls_useIntegrity
    GuardComponent --> IntegrityConstants : reads_thresholds
Loading

Class diagram for on-chain TrustChain notary program and accounts

classDiagram
    class TrustchainNotaryProgram {
        +update_integrity(ctx UpdateIntegrity, gini_score u16, hhi_score u16, status u8) Result
    }

    class UpdateIntegrity {
        +Account~UserIntegrity~ user_integrity
        +UncheckedAccount user
        +Signer notary
        +Program~System~ system_program
    }

    class UserIntegrity {
        +u16 gini_score
        +u16 hhi_score
        +u8 status
        +i64 last_updated
    }

    class GlobalConfig {
        +u8 bump
    }

    class TrustChainError {
        <<enumeration>>
        UnauthorizedNotary
    }

    TrustchainNotaryProgram --> UpdateIntegrity : uses_context
    UpdateIntegrity --> UserIntegrity : initializes_updates
    TrustchainNotaryProgram --> TrustChainError : returns_on_error
    GlobalConfig <.. TrustchainNotaryProgram : PDA_config_account
Loading

File-Level Changes

Change Details Files
Introduce a backend notary synchronization script that signs and submits integrity updates to the on-chain TrustChain notary program using a configurable notary secret.
  • Add notary_sync.ts service that loads NOTARY_SECRET from the environment in JSON-array or hex format and constructs a Keypair
  • Derive the user integrity PDA with the shared "config" seed and encode the update_integrity instruction data using Anchor’s 8-byte discriminator plus packed score fields
  • Build and sign a Solana transaction from the notary keypair, then verify the Ed25519 signature both via web3.js and tweetnacl before logging success
backend/services/notary_sync.ts
Wire the backend to support the notary sync workflow and related Solana libraries.
  • Add a sync:notary npm script to run the notary_sync.ts service via tsx
  • Add @coral-xyz/anchor and tweetnacl as backend dependencies to support Anchor discriminator encoding and explicit signature verification
backend/package.json
Create the Anchor-based on-chain TrustChain notary program that stores integrity scores in a PDA derived with the "config" seed and enforces an authorized notary signer.
  • Define the trustchain_notary program with an update_integrity instruction that updates UserIntegrity fields and timestamps, gated by a hardcoded NOTARY_PUBKEY
  • Configure the UpdateIntegrity accounts to init_if_needed a UserIntegrity PDA at seeds [b"config", user.key().as_ref()] paid by the notary, with extra padding for future upgrades
  • Declare UserIntegrity and GlobalConfig account types plus a custom UnauthorizedNotary error code
  • Set up Anchor and Cargo configuration for the program, including workspace, program crate, and localnet Anchor.toml
on-chain/trustchain-notary/programs/trustchain-notary/src/lib.rs
on-chain/trustchain-notary/programs/trustchain-notary/Cargo.toml
on-chain/trustchain-notary/Cargo.toml
on-chain/trustchain-notary/Anchor.toml
Expose integrity verification results to the frontend and gate UI flows using a reusable React hook and Guard component.
  • Add a useIntegrity hook that POSTs the connected wallet address to /api/verify, tracks gini/hhi scores, status, loading, and error state, and resets when the wallet disconnects
  • Introduce a Guard component that consumes useIntegrity and conditionally blocks children based on a configurable Gini threshold and status flag, rendering a default or provided fallback when blocked
  • Centralize integrity thresholds and sybil status values in a small integrity constants module
frontend/src/hooks/useIntegrity.ts
frontend/src/components/Guard.tsx
frontend/src/constants/integrity.ts
Document the FairScale integration assumptions and environment configuration for the integrity engine.
  • Create FAIRSCALE_API.md outlining a reconstructed FairScale reputation API, response schema, integration logic with TrustChain’s Gini scoring, and required environment variables
docs/FAIRSCALE_API.md
Add configuration scaffolding for backend environment and frontend hosting.
  • Introduce a backend .env.example to document required env vars (e.g., NOTARY_SECRET) and add a Vercel config for the frontend deployment
backend/.env.example
frontend/vercel.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • The notary_sync.ts script mixes runtime configuration with module initialization (e.g., throwing if NOTARY_SECRET is missing and parsing it at import time); consider moving the env validation/parsing into main() so importing or tooling around this file doesn’t immediately crash the process.
  • Several values that are likely environment-specific are currently hardcoded (e.g., the devnet RPC URL in notary_sync.ts, the default API_BASE_URL in useIntegrity, and the program ID/NOTARY_PUBKEY in the on-chain code); it would be safer to drive these from configuration or env vars to avoid accidental misuse across environments.
  • The mapping between integrity status strings in the backend script (VERIFIED, PROBATIONARY, etc.) and the on-chain status: u8 field is implicit—consider extracting a shared constant/enum (or at least a clearly documented mapping) to reduce the risk of mismatches as statuses evolve.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `notary_sync.ts` script mixes runtime configuration with module initialization (e.g., throwing if `NOTARY_SECRET` is missing and parsing it at import time); consider moving the env validation/parsing into `main()` so importing or tooling around this file doesn’t immediately crash the process.
- Several values that are likely environment-specific are currently hardcoded (e.g., the devnet RPC URL in `notary_sync.ts`, the default `API_BASE_URL` in `useIntegrity`, and the program ID/NOTARY_PUBKEY in the on-chain code); it would be safer to drive these from configuration or env vars to avoid accidental misuse across environments.
- The mapping between integrity `status` strings in the backend script (`VERIFIED`, `PROBATIONARY`, etc.) and the on-chain `status: u8` field is implicit—consider extracting a shared constant/enum (or at least a clearly documented mapping) to reduce the risk of mismatches as statuses evolve.

## Individual Comments

### Comment 1
<location> `backend/services/notary_sync.ts:19-28` </location>
<code_context>
+const MOCK_WALLET = new PublicKey("11111111111111111111111111111111");
+
+// Load Notary Secret from Environment Variable
+if (!process.env.NOTARY_SECRET) {
+    throw new Error("NOTARY_SECRET environment variable not set. Please set it in .env file.");
+}
+
+// Support both JSON array format and Hex string format for convenience
+let notarySecret: Uint8Array;
+try {
+    const parsed = JSON.parse(process.env.NOTARY_SECRET);
+    if (Array.isArray(parsed)) {
+        notarySecret = new Uint8Array(parsed);
+    } else {
+        throw new Error("NOTARY_SECRET JSON is not an array");
+    }
+} catch (e) {
+    // If not JSON, try Hex
+    // Remove '0x' prefix if present
+    const cleanHex = process.env.NOTARY_SECRET.replace(/^0x/, '');
+    if (/^[0-9a-fA-F]+$/.test(cleanHex)) {
+         notarySecret = Uint8Array.from(Buffer.from(cleanHex, 'hex'));
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Tighten NOTARY_SECRET validation to avoid ambiguous or malformed key material

Currently any hex string is accepted and only fails later inside `Keypair.fromSecretKey` if the length is wrong. Consider checking that the decoded `Uint8Array` has the expected length (e.g., 64 bytes) before constructing the keypair, and throwing a clear error when it doesn’t.

Suggested implementation:

```typescript
dotenv.config();

const NOTARY_SECRET_KEY_LENGTH = 64;

// Mock data
const MOCK_WALLET = new PublicKey("11111111111111111111111111111111");

```

```typescript
    if (/^[0-9a-fA-F]+$/.test(cleanHex)) {
         notarySecret = Uint8Array.from(Buffer.from(cleanHex, 'hex'));
    } else {
        throw new Error("Invalid NOTARY_SECRET format. Must be JSON array of numbers or Hex string.");
    }
}

if (!notarySecret || notarySecret.length !== NOTARY_SECRET_KEY_LENGTH) {
    throw new Error(
        `NOTARY_SECRET must decode to a ${NOTARY_SECRET_KEY_LENGTH}-byte secret key; got ${notarySecret ? notarySecret.length : 0} bytes.`
    );
}

const NOTARY_KEYPAIR = Keypair.fromSecretKey(notarySecret);

```
</issue_to_address>

### Comment 2
<location> `backend/services/notary_sync.ts:69` </location>
<code_context>
+  // Connect to devnet (or localnet)
+  const connection = new Connection("https://api.devnet.solana.com", "confirmed");
+
+  // PDA derivation: seeds = [b"integrity", user.key().as_ref()]
+  const [pda] = PublicKey.findProgramAddressSync(
+    [Buffer.from("config"), wallet.toBuffer()],
+    PROGRAM_ID
+  );
</code_context>

<issue_to_address>
**suggestion (typo):** Align PDA seed comment with actual implementation (`config` vs `integrity`)

The inline comment still refers to `[b"integrity", user.key().as_ref()]`, but the actual PDA uses `["config", wallet]`. Please update the comment to match the real seeds (`"config"` + wallet) to avoid confusion when working with the PDA or comparing against the on-chain Anchor account definition.

```suggestion
  // PDA derivation: seeds = [b"config", wallet.toBuffer()]
```
</issue_to_address>

### Comment 3
<location> `docs/FAIRSCALE_API.md:18` </location>
<code_context>
+- **URL:** `https://api.fairscale.xyz/v1/reputation/:address` (Hypothetical endpoint)
+- **Method:** `GET`
+- **Params:**
+  - `address`: The Solana wallet address (Base58 encoded).
+
+#### Response
</code_context>

<issue_to_address>
**nitpick (typo):** Consider hyphenating "Base58 encoded" to "Base58-encoded" for grammatical correctness.

Because it modifies “wallet address” as a compound adjective, the hyphenated form is preferred.

```suggestion
  - `address`: The Solana wallet address (Base58-encoded).
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +19 to +28
if (!process.env.NOTARY_SECRET) {
throw new Error("NOTARY_SECRET environment variable not set. Please set it in .env file.");
}

// Support both JSON array format and Hex string format for convenience
let notarySecret: Uint8Array;
try {
const parsed = JSON.parse(process.env.NOTARY_SECRET);
if (Array.isArray(parsed)) {
notarySecret = new Uint8Array(parsed);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Tighten NOTARY_SECRET validation to avoid ambiguous or malformed key material

Currently any hex string is accepted and only fails later inside Keypair.fromSecretKey if the length is wrong. Consider checking that the decoded Uint8Array has the expected length (e.g., 64 bytes) before constructing the keypair, and throwing a clear error when it doesn’t.

Suggested implementation:

dotenv.config();

const NOTARY_SECRET_KEY_LENGTH = 64;

// Mock data
const MOCK_WALLET = new PublicKey("11111111111111111111111111111111");
    if (/^[0-9a-fA-F]+$/.test(cleanHex)) {
         notarySecret = Uint8Array.from(Buffer.from(cleanHex, 'hex'));
    } else {
        throw new Error("Invalid NOTARY_SECRET format. Must be JSON array of numbers or Hex string.");
    }
}

if (!notarySecret || notarySecret.length !== NOTARY_SECRET_KEY_LENGTH) {
    throw new Error(
        `NOTARY_SECRET must decode to a ${NOTARY_SECRET_KEY_LENGTH}-byte secret key; got ${notarySecret ? notarySecret.length : 0} bytes.`
    );
}

const NOTARY_KEYPAIR = Keypair.fromSecretKey(notarySecret);

// Connect to devnet (or localnet)
const connection = new Connection("https://api.devnet.solana.com", "confirmed");

// PDA derivation: seeds = [b"integrity", user.key().as_ref()]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (typo): Align PDA seed comment with actual implementation (config vs integrity)

The inline comment still refers to [b"integrity", user.key().as_ref()], but the actual PDA uses ["config", wallet]. Please update the comment to match the real seeds ("config" + wallet) to avoid confusion when working with the PDA or comparing against the on-chain Anchor account definition.

Suggested change
// PDA derivation: seeds = [b"integrity", user.key().as_ref()]
// PDA derivation: seeds = [b"config", wallet.toBuffer()]

Comment thread docs/FAIRSCALE_API.md
- **URL:** `https://api.fairscale.xyz/v1/reputation/:address` (Hypothetical endpoint)
- **Method:** `GET`
- **Params:**
- `address`: The Solana wallet address (Base58 encoded).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Consider hyphenating "Base58 encoded" to "Base58-encoded" for grammatical correctness.

Because it modifies “wallet address” as a compound adjective, the hyphenated form is preferred.

Suggested change
- `address`: The Solana wallet address (Base58 encoded).
- `address`: The Solana wallet address (Base58-encoded).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant