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
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ packages/api/src/client
packages/lexicon-resolver/src/client
packages/bsky/src/lexicon
packages/pds/src/lexicon
packages/sds/src/lexicon
packages/ozone/src/lexicon
10 changes: 5 additions & 5 deletions .github/workflows/repo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json
node-version-file: '.nvmrc'
- name: Enable Corepack
run: corepack enable
- name: Configure Dependency Cache
Expand Down Expand Up @@ -56,7 +56,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json
node-version-file: '.nvmrc'
- name: Enable Corepack
run: corepack enable
- name: Configure Dependency Cache
Expand All @@ -70,11 +70,11 @@ jobs:
with:
path: ~/.cache
key: ${{ env.CURRENT_MONTH }}-${{ runner.os }}
- run: pnpm i --frozen-lockfile
- uses: actions/download-artifact@v4
with:
name: dist
path: packages
- run: pnpm i --frozen-lockfile
- run: pnpm test:withFlags --maxWorkers=1 --shard=${{ matrix.shard }} --passWithNoTests
verify:
name: Verify
Expand All @@ -86,7 +86,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json
node-version-file: '.nvmrc'
- name: Enable Corepack
run: corepack enable
- name: Configure Dependency Cache
Expand All @@ -100,9 +100,9 @@ jobs:
with:
path: ~/.cache
key: ${{ env.CURRENT_MONTH }}-${{ runner.os }}
- run: pnpm i --frozen-lockfile
- uses: actions/download-artifact@v4
with:
name: dist
path: packages
- run: pnpm i --frozen-lockfile
- run: pnpm verify
21 changes: 21 additions & 0 deletions IMPLEMENTATION-PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Create a `@atproto/sds` package that enables shared data repositories between mu
**Problem Resolved**: The demo application was making direct HTTP fetch calls instead of using AT Protocol lexicons properly.

**Solutions Implemented**:

- ✅ **Fixed SDS Agent**: Updated `lib/sds-agent.ts` with correct lexicon definitions matching server-side specifications
- ✅ **Fixed Organization Creation**: Replaced direct fetch with `agent.call('com.sds.organization.create', ...)`
- ✅ **Fixed Organization Listing**: Replaced direct fetch with `agent.call('com.sds.organization.list', ...)`
Expand All @@ -54,17 +55,20 @@ Create a `@atproto/sds` package that enables shared data repositories between mu
**Key Changes Made**:

1. **Lexicon Definitions Updated** in `packages/sds-demo/src/lib/sds-agent.ts`:

- Added missing `com.sds.organization.list` lexicon
- Updated `com.sds.organization.create` to match server requirements (added `creatorDid`)
- Fixed lexicon types to use proper references
- Implemented smart routing with direct HTTP calls to SDS server

2. **Repository Dashboard Fixed** in `packages/sds-demo/src/components/repository-dashboard.tsx`:

- Replaced `fetch()` call with `auth.agent.call('com.sds.organization.create', ...)`
- Proper error handling through AT Protocol error system
- Better type safety and validation

3. **Query Hooks Fixed** in `packages/sds-demo/src/queries/use-sds-queries.ts`:

- Replaced `fetch()` call with `auth.agent.call('com.sds.organization.list', ...)`
- Improved error handling and fallback behavior
- Better integration with React Query caching
Expand All @@ -75,6 +79,7 @@ Create a `@atproto/sds` package that enables shared data repositories between mu
- Smart routing between servers based on method namespaces

### Testing Results ✅

- ✅ **Organization Creation**: Working end-to-end via lexicon calls
- ✅ **Organization Listing**: Working end-to-end via lexicon calls
- ✅ **Build Process**: All packages compile successfully using Makefile
Expand All @@ -84,13 +89,15 @@ Create a `@atproto/sds` package that enables shared data repositories between mu
## What's Currently Working ✅

### Core SDS Server Infrastructure - **PRODUCTION READY**

- ✅ **SDS Class**: Extends PDS with full shared repository functionality (`packages/sds/src/index.ts`)
- ✅ **Permission Manager**: Complete RBAC system with audit logging (`packages/sds/src/permission-manager/index.ts`)
- ✅ **Enhanced Authentication**: SDS auth verifier with cross-repository permission checks (`packages/sds/src/sds-auth-verifier.ts`)
- ✅ **Database Schema**: Multi-user permissions and audit logging tables
- ✅ **API Endpoints**: All SDS-specific endpoints working (`packages/sds/src/api/com/sds/`)

### Demo Application - **PRODUCTION READY** ✅

- ✅ **OAuth Authentication**: Working with both PDS and SDS servers
- ✅ **Organization Creation**: Create shared repositories through proper lexicon calls - **TESTED**
- ✅ **Organization Listing**: List user's organizations with proper permissions - **TESTED**
Expand All @@ -100,6 +107,7 @@ Create a `@atproto/sds` package that enables shared data repositories between mu
- ✅ **Error Handling**: Robust error handling and fallback behavior

### API Endpoints - **ALL WORKING**

- ✅ `com.sds.organization.create` - Create organizations with proper repository DIDs
- ✅ `com.sds.organization.list` - List organizations user has access to
- ✅ `com.sds.repo.grantAccess` - Grant repository permissions to users
Expand All @@ -114,39 +122,45 @@ Create a `@atproto/sds` package that enables shared data repositories between mu
**Goal**: Implement repository sharing functionality using direct HTTP calls to establish core collaboration features.

#### **1.1 Collaboration Service Layer** - ✅ **COMPLETED**

- ✅ **File Created**: `packages/sds-demo/src/services/collaboration-service.ts`
- ✅ **Direct HTTP Methods**: `grantRepositoryAccess()`, `revokeRepositoryAccess()`, `listRepositoryCollaborators()`, `getRepositoryPermissions()`
- ✅ **Type Definitions**: Comprehensive TypeScript interfaces for all collaboration operations
- ✅ **Error Handling**: Retry logic and comprehensive error reporting
- ✅ **Utility Functions**: DID validation, name formatting, permission level display

#### **1.2 React Query Integration** - ✅ **COMPLETED**

- ✅ **File Created**: `packages/sds-demo/src/queries/use-collaboration-queries.ts`
- ✅ **Hooks**: `useGrantAccessMutation()`, `useRevokeAccessMutation()`, `useListCollaboratorsQuery()`, `useCanManageRepository()`
- ✅ **Cache Management**: Optimistic updates for grants, pessimistic updates for revokes
- ✅ **Integration**: Comprehensive error handling, loading states, and query key factory pattern

#### **1.3 UI Components** - ✅ **COMPLETED**

- ✅ **Collaboration Modal**: Full-featured dialog with tabbed interface for repository sharing management (`packages/sds-demo/src/components/collaboration-modal.tsx`)
- ✅ **Grant Access Form**: DID validation, permission selection checkboxes, form validation
- ✅ **Collaborator List**: Display current collaborators with permissions, grant dates, and revoke actions
- ✅ **Permission Badges**: Visual indicators for permission levels with detailed breakdown (`packages/sds-demo/src/components/permission-badge.tsx`)
- ✅ **Repository Cards**: Enhanced cards with collaboration features and management buttons (`packages/sds-demo/src/components/repository-card.tsx`)

#### **1.4 Repository Dashboard Integration** - ✅ **COMPLETED**

- ✅ **Enhanced Repository Cards**: "Manage" button for owned repositories, permission displays
- ✅ **Collaborator Count**: Real-time display of collaborator count via API integration
- ✅ **Repository Context**: Enhanced context with `updateCollaborators()` and `refreshRepository()` methods
- ✅ **Modal Integration**: Seamless modal state management for collaboration workflows

#### **1.5 Repository Context Updates** - ✅ **COMPLETED**

- ✅ **Enhanced Repository Interface**: Added `collaboratorCount`, `isOwner`, `createdAt`, `description` fields
- ✅ **Context Methods**: `updateCollaborators()` and `refreshRepository()` for dynamic updates
- ✅ **State Integration**: Seamless integration with collaboration features and real-time updates

### **Phase 1 Status: PRODUCTION READY** ✅

**Summary**: Repository collaboration is fully functional with direct HTTP calls to SDS endpoints. Users can:

- Create shared repositories
- Grant read/write permissions to other users via DID
- View and manage collaborators with full UI
Expand All @@ -157,41 +171,48 @@ Create a `@atproto/sds` package that enables shared data repositories between mu
**Integration**: ✅ Fully integrated with existing demo app infrastructure

#### **1.6 End-to-End Testing** - ✅ **COMPLETED**

- ✅ **Build Verification**: All packages build successfully without errors
- ✅ **Component Integration**: Repository cards, collaboration modal, and permission badges work together
- ✅ **State Management**: Repository context properly manages collaboration data
- ✅ **API Integration**: React Query hooks successfully communicate with SDS collaboration endpoints

### **Phase 1 Success Criteria** ✅ **ALL MET**

- ✅ Repository owners can grant read/write access to other users via DID
- ✅ Collaborators appear in repository dashboard with correct permissions
- ✅ Users can revoke access from collaborators
- ✅ Collaboration UI integrates seamlessly with existing repository dashboard
- ✅ All collaboration features work with direct HTTP calls

### **Phase 2: JWT Claims & OAuth Integration** - 🔮 **FUTURE**

**Goal**: Migrate to production-ready authentication with proper JWT claims validation.

#### **2.1 Authentication Architecture** - 🔮 **FUTURE**

- 🔮 **JWT Token Handling**: SdsAgent enhanced to handle JWT authentication
- 🔮 **Cross-Server Validation**: SDS validates JWT tokens issued by PDS
- 🔮 **Claims Mapping**: Map PDS identity to SDS repository permissions

#### **2.2 Migration to Authenticated Lexicon Calls** - 🔮 **FUTURE**

- 🔮 **Replace HTTP Calls**: Update collaboration service to use `agent.call()` with authentication
- 🔮 **Token Refresh**: Automatic token refresh logic for long-running sessions
- 🔮 **Error Handling**: Proper authentication error handling and user feedback

## Remaining Work (5% of total)

### Current Sprint (Phase 1)

1. **✅ Collaboration Service**: Direct HTTP calls to SDS endpoints - **COMPLETED**
2. **🚧 React Query Hooks**: Collaboration query and mutation hooks - **IN PROGRESS**
3. **📋 UI Components**: Collaboration forms and collaborator management
4. **📋 Dashboard Integration**: Enhanced repository dashboard with collaboration
5. **📋 Testing**: End-to-end collaboration functionality testing

### Future Work

1. **Phase 2 Migration**: JWT authentication and lexicon-based calls
2. **Content Creation**: Enable creating posts/records in shared repositories
3. **Advanced Permissions**: Role-based access beyond read/write
Expand Down
2 changes: 1 addition & 1 deletion lexicons/com/sds/organization/list.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,4 @@
}
}
}
}
}
1 change: 0 additions & 1 deletion packages/dev-infra/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: '3.8'
services:
# An ephermerally-stored postgres database for single-use test runs
db_test: &db_test
Expand Down
4 changes: 1 addition & 3 deletions packages/sds-demo/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,7 @@ module.exports = defineConfig((commandLineArguments) => {
source: JSON.stringify(metadata, null, 2),
})

console.log(
`Generated client-metadata.json for ${clientUrl}`,
)
console.log(`Generated client-metadata.json for ${clientUrl}`)
},
},

Expand Down
1 change: 0 additions & 1 deletion packages/sds-demo/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ function App() {
<RepositoryDashboard />
</div>


{/* Technical Details (collapsible) */}
<details className="rounded-lg bg-gray-50 p-6 shadow-md">
<summary className="cursor-pointer text-lg font-medium text-gray-700">
Expand Down
49 changes: 38 additions & 11 deletions packages/sds-demo/src/components/collaboration-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ export function CollaborationModal({
'collaborators',
)
const [userDid, setUserDid] = useState('')
const [permissions, setPermissions] = useState<RepositoryPermissions>({
const [_permissions, setPermissions] = useState<RepositoryPermissions>({
read: true,
write: false,
create: false,
update: false,
delete: false,
})
const [selectedRole, setSelectedRole] = useState<
'viewer' | 'contributor' | 'admin'
Expand All @@ -45,17 +47,35 @@ export function CollaborationModal({
viewer: {
name: 'Viewer',
description: 'Can view repository content',
permissions: { read: true, write: false, admin: false },
permissions: {
read: true,
create: false,
update: false,
delete: false,
admin: false,
},
},
contributor: {
name: 'Contributor',
description: 'Can view and modify repository content',
permissions: { read: true, write: true, admin: false },
permissions: {
read: true,
create: true,
update: true,
delete: true,
admin: false,
},
},
admin: {
name: 'Admin',
description: 'Full access including user management',
permissions: { read: true, write: true, admin: true },
permissions: {
read: true,
create: true,
update: true,
delete: true,
admin: true,
},
},
}

Expand Down Expand Up @@ -90,7 +110,12 @@ export function CollaborationModal({
// Reset form
setUserDid('')
setSelectedRole('viewer')
setPermissions({ read: true, write: false })
setPermissions({
read: true,
create: false,
update: false,
delete: false,
})
setActiveTab('collaborators') // Switch back to collaborators tab
} catch (error) {
console.error('Failed to grant access:', error)
Expand Down Expand Up @@ -312,10 +337,12 @@ export function CollaborationModal({
handleRevokeAccess(collaborator.userDid)
}
size="small"
disabled={revokeAccessMutation.isLoading}
disabled={
revokeAccessMutation.status === 'pending'
}
className="ml-4 bg-red-600 text-white hover:bg-red-700"
>
{revokeAccessMutation.isLoading &&
{revokeAccessMutation.status === 'pending' &&
revokeAccessMutation.variables?.userDid ===
collaborator.userDid ? (
<Spinner className="h-4 w-4" />
Expand Down Expand Up @@ -398,7 +425,7 @@ export function CollaborationModal({
<div className="flex justify-end space-x-3">
<Button
onClick={() => setActiveTab('collaborators')}
disabled={grantAccessMutation.isLoading}
disabled={grantAccessMutation.status === 'pending'}
>
Cancel
</Button>
Expand All @@ -407,11 +434,11 @@ export function CollaborationModal({
disabled={
!userDid.trim() ||
!validateDid(userDid.trim()) ||
grantAccessMutation.isLoading
grantAccessMutation.status === 'pending'
}
className="bg-blue-600 text-white hover:bg-blue-700"
>
{grantAccessMutation.isLoading ? (
{grantAccessMutation.status === 'pending' ? (
<>
<Spinner className="mr-2 h-4 w-4" />
Adding Collaborator...
Expand Down
8 changes: 2 additions & 6 deletions packages/sds-demo/src/components/repository-dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ import {
Repository,
useRepositoryContext,
} from '../contexts/repository-context.tsx'
import {
useCreateRecordMutation,
useListOrganizationsQuery,
} from '../queries/use-sds-queries.ts'
import { useListOrganizationsQuery } from '../queries/use-sds-queries.ts'
import { RepositoryPermissions } from '../services/collaboration-service.ts'
import { retryApiCall } from '../utils/api-retry.ts'
import { Button } from './button.tsx'
Expand Down Expand Up @@ -50,7 +47,7 @@ export function RepositoryDashboard() {
const { repositories, addRepository, setSelectedRepo } =
useRepositoryContext()
const [loading, setLoading] = useState(false)
const [selectedRepo, setSelectedRepoLocal] = useState<string | null>(null)
const [selectedRepo] = useState<string | null>(null)
const [newPostText, setNewPostText] = useState('')
const [showCreateOrg, setShowCreateOrg] = useState(false)
const [newOrgName, setNewOrgName] = useState('')
Expand All @@ -65,7 +62,6 @@ export function RepositoryDashboard() {
repositoryHandle: '',
})

const createRecordMutation = useCreateRecordMutation()
const organizationsQuery = useListOrganizationsQuery()

// Helper functions for collaboration modal
Expand Down
2 changes: 1 addition & 1 deletion packages/sds-demo/src/contexts/repository-context.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ReactNode, createContext, useContext, useState } from 'react'
import { ReactNode, createContext, useContext, useState } from 'react'
import { RepositoryPermissions } from '../services/collaboration-service.ts'

export interface Repository {
Expand Down
1 change: 1 addition & 0 deletions packages/sds-demo/src/lib/sds-agent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Agent } from '@atproto/api'
import { LexiconDoc } from '@atproto/lexicon'
import { OAuthSession, dpopFetchWrapper } from '@atproto/oauth-client'
// eslint-disable-next-line import/no-unresolved
import { Fetch } from '@atproto-labs/fetch'
import { ENV, SDS_SERVER_URL } from '../constants.ts'

Expand Down
Loading
Loading