Skip to content

Conversation

@TaylorBeeston
Copy link
Collaborator

@TaylorBeeston TaylorBeeston commented Dec 18, 2025

Overview

🎟 Relevant Jira Issues

LC-1472 Simplify read/write to consent flow contract API pattern

📚 What is the context and goal of this PR?

One of the challenges with integration right now is that it's just plain a pain to send credentials
through a contract! Especially if you're using a signing authority. This PR aims to create a single,
ergonomic send method that handles sending a credential gracefully for every possible scenario. This
means like if you want to send through a contract, you can, if you want to send a single VC, you can,
if you want to send a boost, you can, etc. without having to think through a lot of the steps involved,
just supply what you have! I also made this extensible using a discriminated union so that in the future
we can use for sending other things like VPs, etc!

🥴 TL; RL:

Adds a new generic send method that provides an ergonomic way to send credentials in the LearnCard Network. It handles boost template resolution, credential issuance (client-side when available, otherwise via signing authority), and consent flow integration automatically.

  • New send method for ergonomic credential sending
  • Client-side issuance when possible, falls back to signing authority
  • Contract-aware: routes through consent flow when recipient has consented
  • Creates RELATED_TO relationship between new boosts and contracts
  • Discriminated union pattern for future extensibility (e.g., other send types)

💡 Feature Breakdown (screenshots & videos encouraged!)

New send API:

// Send using existing boost template
await lc.invoke.send({
    type: 'boost',
    recipient: 'userProfileId', // or DID
    templateUri: 'urn:lc:boost:...',
});

// Send by creating a new boost on-the-fly
await lc.invoke.send({
    type: 'boost',
    recipient: 'userProfileId',
    template: { credential: unsignedVC, category: 'Achievement' },
    contractUri: 'urn:lc:contract:...', // optional - routes through consent flow
});

// Returns: { type: 'boost', credentialUri: '...', uri: '...' }

Key features:

  • recipient accepts either profileId or DID
  • Client-side credential issuance when issueCredential is available in the LearnCard instance
  • Falls back to signing authority for API token users or when client-side issuance unavailable
  • Contract integration with automatic consent flow routing

🛠 Important tradeoffs made:

  • TRPC doesn't support z.discriminatedUnion as input validators, so we use separate validators:
    • Route-level: SendBoostInputValidator (ZodObject - TRPC compatible)
    • Plugin-level: SendInputValidator (discriminated union - for type extensibility)
  • The type field is required (no default) due to discriminated union constraints

🔍 Types of Changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Chore (refactor, documentation update, etc)

💳 Does This Create Any New Technical Debt? ( If yes, please describe and add JIRA TODOs )

  • No
  • Yes

Testing

🔬 How Can Someone QA This?

  1. Build types: pnpm exec nx build types
  2. Run unit tests: cd services/learn-card-network/brain-service && pnpm test -- --run -t "send" --no-coverage
  3. Run E2E tests: cd tests/e2e && pnpm test:e2e -- --run -t "Send"
  4. Test manually by calling learnCard.invoke.send({ type: 'boost', recipient: '<profileId>', templateUri: '<boostUri>' })

📱 🖥 Which devices would you like help testing on?

N/A - Backend/SDK changes only

🧪 Code Coverage

  • Unit tests added in boosts.spec.ts for send method
  • Unit tests added in consentflow.spec.ts for contract integration
  • E2E tests added in ergonomic-send-boost.spec.ts

Documentation

📜 Gitbook

Documentation needed for the new send method API.

📊 Storybook

N/A

✅ PR Checklist

  • Related to a Jira issue (create one if not)
  • My code follows style guidelines (eslint / prettier)
  • I have manually tested common end-2-end cases
  • I have reviewed my code
  • I have commented my code, particularly where ambiguous
  • New and existing unit tests pass locally with my changes
  • I have made corresponding changes to gitbook documentation

🚀 Ready to squash-and-merge?:

  • Code is backwards compatible
  • There is not a "Do Not Merge" label on this PR
  • I have thoughtfully considered the security implications of this change.
  • This change does not expose new public facing endpoints that do not have authentication

✨ PR Description

Purpose: Add ergonomic send method to simplify boost credential issuance and delivery with automatic signing authority fallback and consent flow integration.

Main changes:

  • Implemented send method supporting template-based or on-the-fly boost credential creation with auto-issuance
  • Added contract-aware routing that automatically uses consent flow when recipient has consented with write permissions
  • Created RELATED_TO relationship between newly created boosts and contracts for better traceability
  • Updated documentation with comprehensive guides for send method usage and contract integration patterns

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Description using Guidelines Learn how

@changeset-bot
Copy link

changeset-bot bot commented Dec 18, 2025

🦋 Changeset detected

Latest commit: e389432

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 46 packages
Name Type
@learncard/network-brain-service Minor
@learncard/network-plugin Minor
@learncard/types Minor
@learncard/network-brain-client Patch
@learncard/init Patch
@learncard/chapi-example Patch
@learncard/snap-example-dapp Patch
@learncard/lca-api-client Patch
@learncard/create-http-bridge Patch
@learncard/cli Patch
@learncard/core Patch
@learncard/helpers Patch
@learncard/react Patch
@learncard/learn-cloud-client Patch
@learncard/simple-signing-client Patch
@learncard/ceramic-plugin Patch
@learncard/chapi-plugin Patch
@learncard/claimable-boosts-plugin Patch
@learncard/did-web-plugin Patch
@learncard/didkey-plugin Patch
@learncard/didkit-plugin Patch
@learncard/encryption-plugin Patch
@learncard/idx-plugin Patch
@learncard/learn-card-plugin Patch
@learncard/learn-cloud-plugin Patch
@learncard/linked-claims-plugin Patch
@learncard/open-badge-v2-plugin Patch
@learncard/vc-api-plugin Patch
@learncard/vc-templates-plugin Patch
@learncard/vc-plugin Patch
@learncard/vpqr-plugin Patch
learn-card-discord-bot Patch
@learncard/meta-mask-snap Patch
@learncard/lca-api-service Patch
@learncard/learn-cloud-service Patch
@learncard/simple-signing-service Patch
learn-card-app Patch
scoutpass-app Patch
@learncard/snap-chapi-example Patch
learn-card-base Patch
@learncard/expiration-plugin Patch
@learncard/crypto-plugin Patch
@learncard/dynamic-loader-plugin Patch
@learncard/ethereum-plugin Patch
@learncard/lca-api-plugin Patch
@learncard/simple-signing-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify
Copy link

netlify bot commented Dec 18, 2025

Deploy Preview for learncarddocs canceled.

Name Link
🔨 Latest commit e389432
🔍 Latest deploy log https://app.netlify.com/projects/learncarddocs/deploys/69457122fc5d9b0009c9b5de

@github-actions
Copy link
Contributor

👋 Hey there! It looks like you modified code, but didn't update the documentation in /docs.

If this PR introduces new features, changes APIs, or modifies behavior that users or developers need to know about, please consider updating the docs.


🏄 Windsurf Tip

You can ask Windsurf to help:

"Analyze the changes in this PR and update the gitbook docs in /docs accordingly."

Windsurf will review your changes and suggest appropriate documentation updates based on what was modified.


📚 Documentation Guide
Change Type Doc Location
New feature/API docs/tutorials/ or docs/how-to-guides/
SDK/API changes docs/sdks/
New concepts docs/core-concepts/
App UI/UX flows docs/apps/ (LearnCard App, ScoutPass)
Internal patterns CLAUDE.md

This is an automated reminder. If no docs are needed, feel free to ignore this message.

@netlify
Copy link

netlify bot commented Dec 18, 2025

Deploy Preview for staging-learncardapp ready!

Name Link
🔨 Latest commit e389432
🔍 Latest deploy log https://app.netlify.com/projects/staging-learncardapp/deploys/694571223bbe72000802756b
😎 Deploy Preview https://deploy-preview-895--staging-learncardapp.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Contributor

@gitstream-cm gitstream-cm bot left a comment

Choose a reason for hiding this comment

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

✨ PR Review

The PR introduces a comprehensive ergonomic send method with proper error handling and security checks. The server-side implementation is well-structured with appropriate fallbacks and validation. However, there's a concerning type cast that bypasses type safety.

1 issues detected:

🧹 Maintainability - Unnecessary type assertion that bypasses type safety without clear justification 🛠️

Details: The code uses as any to bypass TypeScript's type checking when calling the boost.send.mutate method. Since SendBoostInput already includes an optional signedCredential field, this cast appears unnecessary and reduces type safety.
File: packages/plugins/learn-card-network/src/plugin.ts (1008-1008)
🛠️ A suggested code correction is included in the review comments.

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Review using Guidelines Learn how

@Custard7
Copy link
Collaborator

@claude please review and provide a recommendation for merging

@claude
Copy link
Contributor

claude bot commented Dec 18, 2025

Claude finished @Custard7's task —— View job


PR Review: LC-1472 Simplify Send Ergonomics

I'll review this PR and provide a merge recommendation. Here's my analysis:

Todo List:

  • Read and understand key implementation changes
  • Analyze the new send method architecture
  • Review type safety and the flagged as any cast issue
  • Evaluate test coverage and quality
  • Assess security implications
  • Check for proper error handling and edge cases
  • Provide final merge recommendation

🔍 Detailed Review

✅ Architecture & Design

The PR introduces a well-architected ergonomic send system:

  • Smart Dual-Validator Pattern: Uses SendBoostInputValidator (ZodObject for tRPC) and SendInputValidator (discriminated union for plugin extensibility) - this is a clever workaround for tRPC's discriminated union limitations
  • Intelligent Fallback Logic: Client-side credential issuance when issueCredential is available, otherwise falls back to signing authority
  • Contract-Aware Routing: Properly validates consent terms, writer permissions, and category allowances before routing through consent flow
  • Future-Proof Design: Discriminated union pattern enables clean extension for VPs and other send types

✅ Implementation Quality

Strong Points:

  1. Comprehensive Input Validation: templateUri XOR template refinement ensures exactly one is provided
  2. Robust Permission Checks: Validates boost issuance permissions, draft status, and writer authorization
  3. Proper Error Handling: Clear, contextual error messages with appropriate HTTP codes
  4. Graph Relationship Management: Creates RELATED_TO relationships for contract-boost associations when creating new boosts
  5. Security-First Approach: Blocks relationships, validates consent terms, and respects denied writers

Areas for Improvement:

  1. Type Safety Issue (plugin.ts:1008): The as any cast bypasses TypeScript safety when adding signedCredential to the input object. Since SendBoostInput already includes signedCredential?: VC, this cast appears unnecessary.

✅ Test Coverage

Excellent test coverage across multiple layers:

Unit Tests (boosts.spec.ts):

  • ✅ Basic send scenarios (existing boost, create new, DID vs profileId)
  • ✅ Input validation (missing fields, unauthorized users)
  • ✅ Draft boost rejection

Unit Tests (consentflow.spec.ts):

  • ✅ Contract routing when consented vs not consented
  • ✅ Relationship creation for new boosts with contracts
  • ✅ Fallback scenarios (unauthorized writers, disallowed categories)

E2E Tests (ergonomic-send-boost.spec.ts):

  • ✅ Full integration testing with actual signing authorities
  • ✅ Contract consent flows end-to-end

✅ Security Assessment

Security implementation is solid:

  • Authentication Required: All routes use profileRoute with proper auth
  • Permission Validation: Checks boost issuance permissions and writer authorization
  • Relationship Blocking: Respects blocked relationships
  • Consent Validation: Verifies contract terms and category permissions
  • Signing Authority Fallback: Secure credential issuance when client-side unavailable

⚠️ Issues to Address

1. Type Safety (Minor)

// Line 1008 in plugin.ts - unnecessary type cast
return client.boost.send.mutate({
    ...input,
    signedCredential,
} as any); // <- Should be removed

Recommendation: Remove the as any cast as SendBoostInput.signedCredential is already optional.

2. Error Handling (Minor)
The plugin fallback logic could be clearer when client-side issuance fails vs when it's unavailable.


📊 Change Summary

  • 941 additions, 18 deletions across 10 files
  • New ergonomic send method with intelligent routing
  • Comprehensive test coverage (unit + E2E)
  • Future-extensible architecture with discriminated unions
  • Contract-aware credential routing with proper consent validation

🚀 Merge Recommendation: ✅ APPROVE

This PR should be merged after addressing the minor type safety issue. Here's why:

Strengths:

  1. Excellent Architecture: Well-designed, extensible, and follows LearnCard patterns
  2. Comprehensive Testing: Strong unit and E2E test coverage
  3. Security-First: Proper authentication, authorization, and validation
  4. Real User Value: Significantly simplifies credential sending workflow
  5. Backward Compatible: Doesn't break existing functionality

Minor Fixes Needed:

  1. Remove the as any cast in plugin.ts:1008
  2. Consider adding inline documentation for the dual-validator pattern rationale

Future Considerations:

  • Monitor the tRPC + discriminated union limitation - this may be resolved in future tRPC versions
  • Consider extracting contract routing logic into a separate helper for reusability

Overall: This is high-quality code that delivers significant ergonomic improvements while maintaining security and extensibility. The implementation demonstrates deep understanding of the LearnCard architecture and properly handles complex consent flow scenarios.


Review completed ✅

Job run

Copy link
Contributor

@gitstream-cm gitstream-cm bot left a comment

Choose a reason for hiding this comment

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

✨ PR Review

The PR successfully implements an ergonomic send method for the LearnCard Network with good extensibility through discriminated unions. The implementation handles both client-side and server-side credential issuance appropriately, with proper contract integration for consent flow scenarios.

1 issues detected:

🐞 Bug - Direct property assignment on potentially null/undefined credentialSubject will cause runtime errors 🛠️

Details: The code directly assigns to boost.credentialSubject.id without checking if credentialSubject is null or undefined. While Array.isArray() returns false for null/undefined values, the subsequent property assignment will throw a runtime error if credentialSubject is falsy.
File: packages/plugins/learn-card-network/src/plugin.ts (997-997)
🛠️ A suggested code correction is included in the review comments.

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Review using Guidelines Learn how

Copy link
Contributor

@gitstream-cm gitstream-cm bot left a comment

Choose a reason for hiding this comment

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

✨ PR Review

The PR introduces a well-designed ergonomic send method with comprehensive error handling and contract integration. The discriminated union pattern for extensibility is a good architectural choice.

1 issues detected:

🐞 Bug - Spreading null or undefined values causes runtime errors 🛠️

Details: The code spreads the credentialSubject object without checking if it's null or undefined. If credentialSubject is null/undefined, the spread operator will throw a TypeError at runtime.
File: services/learn-card-network/brain-service/src/routes/boosts.ts (675-675)
🛠️ A suggested code correction is included in the review comments.

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Review using Guidelines Learn how

@gitstream-cm
Copy link
Contributor

gitstream-cm bot commented Dec 18, 2025

🥷 Code experts: Custard7

Custard7, TaylorBeeston have most 👩‍💻 activity in the files.
TaylorBeeston, Custard7 have most 🧠 knowledge in the files.

See details

docs/core-concepts/consent-and-permissions/writing-consented-data.md

Activity based on git-commit:

Custard7 TaylorBeeston
DEC 77 additions & 0 deletions
NOV
OCT
SEP
AUG
JUL

Knowledge based on git-blame:
Custard7: 100%

docs/how-to-guides/send-credentials.md

Activity based on git-commit:

Custard7 TaylorBeeston
DEC 334 additions & 0 deletions
NOV
OCT
SEP
AUG
JUL

Knowledge based on git-blame:
Custard7: 100%

docs/sdks/learncard-network/usage-examples.md

Activity based on git-commit:

Custard7 TaylorBeeston
DEC 30 additions & 0 deletions
NOV
OCT
SEP
AUG
JUL

Knowledge based on git-blame:
Custard7: 100%

docs/tutorials/create-a-boost.md

Activity based on git-commit:

Custard7 TaylorBeeston
DEC 393 additions & 1 deletions
NOV
OCT
SEP
AUG
JUL

Knowledge based on git-blame:
Custard7: 100%

packages/learn-card-types/src/lcn.ts

Activity based on git-commit:

Custard7 TaylorBeeston
DEC 178 additions & 81 deletions
NOV 550 additions & 64 deletions
OCT 94 additions & 1 deletions
SEP
AUG 6 additions & 2 deletions 19 additions & 1 deletions
JUL 208 additions & 1 deletions 5 additions & 4 deletions

Knowledge based on git-blame:
Custard7: 64%
TaylorBeeston: 35%

packages/plugins/learn-card-network/src/plugin.ts

Activity based on git-commit:

Custard7 TaylorBeeston
DEC 109 additions & 0 deletions
NOV 185 additions & 14 deletions
OCT 47 additions & 0 deletions
SEP 265 additions & 149 deletions
AUG 40 additions & 0 deletions
JUL 44 additions & 0 deletions 37 additions & 6 deletions

Knowledge based on git-blame:
Custard7: 62%
TaylorBeeston: 29%

packages/plugins/learn-card-network/src/types.ts

Activity based on git-commit:

Custard7 TaylorBeeston
DEC 50 additions & 0 deletions
NOV 130 additions & 5 deletions
OCT 26 additions & 2 deletions
SEP 10 additions & 7 deletions
AUG 2 additions & 1 deletions 17 additions & 0 deletions
JUL 26 additions & 0 deletions 13 additions & 0 deletions

Knowledge based on git-blame:
Custard7: 65%
TaylorBeeston: 22%

services/learn-card-network/brain-service/src/accesslayer/consentflowcontract/relationships/create.ts

Activity based on git-commit:

Custard7 TaylorBeeston
DEC
NOV 4 additions & 0 deletions
OCT
SEP
AUG
JUL 10 additions & 0 deletions

Knowledge based on git-blame:
Custard7: 84%
TaylorBeeston: 4%

services/learn-card-network/brain-service/src/models/ConsentFlowContract.ts

Activity based on git-commit:

Custard7 TaylorBeeston
DEC
NOV
OCT
SEP
AUG
JUL

Knowledge based on git-blame:
Custard7: 65%

services/learn-card-network/brain-service/src/routes/boosts.ts

Activity based on git-commit:

Custard7 TaylorBeeston
DEC 13 additions & 11 deletions
NOV 415 additions & 5 deletions
OCT 11 additions & 3 deletions
SEP 4 additions & 2 deletions
AUG 111 additions & 0 deletions
JUL 81 additions & 3 deletions

Knowledge based on git-blame:
Custard7: 74%
TaylorBeeston: 26%

services/learn-card-network/brain-service/test/boosts.spec.ts

Activity based on git-commit:

Custard7 TaylorBeeston
DEC
NOV 555 additions & 20 deletions
OCT
SEP
AUG 1242 additions & 449 deletions
JUL

Knowledge based on git-blame:
Custard7: 73%
TaylorBeeston: 26%

services/learn-card-network/brain-service/test/consentflow.spec.ts

Activity based on git-commit:

Custard7 TaylorBeeston
DEC
NOV
OCT 51 additions & 0 deletions
SEP
AUG
JUL 45 additions & 0 deletions

Knowledge based on git-blame:
Custard7: 96%
TaylorBeeston: 2%

tests/e2e/tests/skills.e2e.spec.ts

Activity based on git-commit:

Custard7 TaylorBeeston
DEC
NOV 1637 additions & 0 deletions
OCT
SEP
AUG
JUL

Knowledge based on git-blame:
TaylorBeeston: 100%

✨ Comment /gs review for LinearB AI review. Learn how to automate it here.

@smurflo2
Copy link
Collaborator

@TaylorBeeston I got a bunch of errors when running the e2e tests. I fixed some that didn't sync over from the skill frameworks PR, but there were a bunch of others. No pic bc docker's super slow (/stuck?), but I'll add one when I can get it to run

Copy link
Collaborator

@Custard7 Custard7 left a comment

Choose a reason for hiding this comment

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

All tests pass, and working for me - excellent implementation ✅ 🚀

Co-authored-by: gitstream-cm[bot] <111687743+gitstream-cm[bot]@users.noreply.github.com>
@Custard7 Custard7 merged commit bb6749d into main Dec 19, 2025
24 of 25 checks passed
@Custard7 Custard7 deleted the LC-1472-Ergonomics branch December 19, 2025 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants