docs(openspec): add FN-007 Git Connection Model change
- Add proposal, design, specs, and tasks for git connection model - Include provider adapter, credential storage, SSH key lifecycle specs - Add repository connection API and git operations specifications
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-16
|
||||
@@ -0,0 +1,79 @@
|
||||
## Context
|
||||
|
||||
The platform has a Git abstraction layer in `apps/api/app/git/` with:
|
||||
- `provider.py`: GitProvider ABC (validate_connection, list_repos, create_deploy_key, delete_deploy_key, get_default_branch)
|
||||
- `connection.py`: ConnectionManager (connect/disconnect/get_connection)
|
||||
- `credentials.py`: GitCredential, AccessTokenCredential, CredentialStorage ABC
|
||||
- `ssh_key.py`: SshKeyPair, SshKeyLifecycle with Ed25519 generation
|
||||
- `operations.py`: GitOperations ABC, LocalGitOperations (clone/fetch/push are NotImplementedError)
|
||||
- `types.py`: ProviderKind, CredentialKind, ConnectionStatus, SshKeyStatus enums
|
||||
|
||||
Current gaps:
|
||||
- No concrete provider adapters (GitHub, GitLab)
|
||||
- CredentialStorage has no database implementation
|
||||
- LocalGitOperations is incomplete
|
||||
- No RepositoryConnection router or API endpoints
|
||||
- SSH key generation uses base64 placeholder
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Implement concrete GitHub and GitLab provider adapters
|
||||
- Create database-backed credential storage with encryption
|
||||
- Complete LocalGitOperations with credential-aware subprocess
|
||||
- Add RepositoryConnection router with CRUD endpoints
|
||||
- Generate Ed25519 SSH keys and register deploy keys
|
||||
- Frontend UI for repository connections and SSH key management
|
||||
|
||||
**Non-Goals:**
|
||||
- Support for Gitea/Forgejo (deferred post-MVP)
|
||||
- GitHub/GitLab OAuth app integration (use personal access tokens)
|
||||
- Webhook management
|
||||
- Repository mirroring
|
||||
- Branch protection management
|
||||
|
||||
## Decisions
|
||||
|
||||
**1. Use httpx for provider API calls**
|
||||
- Rationale: Already in dependencies, async support, consistent with FastAPI
|
||||
- Alternative: requests - blocking, would need thread pool
|
||||
|
||||
**2. Store credentials encrypted with Fernet (same as secrets)**
|
||||
- Rationale: Consistent with existing secret storage in FN-009
|
||||
- Implementation: Reuse encryption service from app/encryption.py
|
||||
|
||||
**3. SSH keys generated per-repository (not per-user)**
|
||||
- Rationale: Fine-grained access control, easy revocation per repo
|
||||
- Alternative: Per-user keys - broader blast radius on compromise
|
||||
|
||||
**4. Provider adapters implement GitProvider ABC**
|
||||
- Rationale: Clean abstraction, easy to add new providers
|
||||
- Implementation: GitHubAdapter, GitLabAdapter with unified interface
|
||||
|
||||
**5. Git operations use subprocess with SSH key in temp file**
|
||||
- Rationale: Standard git CLI is most reliable
|
||||
- Implementation: Write key to temp file, set GIT_SSH_COMMAND env var
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
**[Risk] Personal access tokens have broad permissions**
|
||||
→ Mitigation: Document minimal required scopes (repo read/write, deploy key management)
|
||||
|
||||
**[Risk] SSH keys in temp files are briefly exposed on disk**
|
||||
→ Mitigation: Use 0600 permissions, clean up immediately after operation
|
||||
|
||||
**[Risk] Provider API rate limits**
|
||||
→ Mitigation: Cache repository lists, implement exponential backoff
|
||||
|
||||
**[Risk] Token storage compromise**
|
||||
→ Mitigation: Fernet encryption with rotation support
|
||||
|
||||
## Migration Plan
|
||||
|
||||
No migration. New feature.
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. Should we support SSH key passphrases?
|
||||
2. Do we need to validate repository URLs before connection?
|
||||
3. Should we auto-detect provider from URL?
|
||||
@@ -0,0 +1,39 @@
|
||||
## Why
|
||||
|
||||
The platform needs a provider-independent Git connection model so users can connect repositories from GitHub, GitLab, Gitea, or Forgejo without vendor lock-in. Currently, the Git abstraction layer exists but lacks concrete provider adapters, credential storage, and API endpoints for managing connections. This is the foundation for repository cloning, branch management, and automated deploy key registration.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Provider adapter framework**: Concrete implementations for GitHub and GitLab APIs with unified interface
|
||||
- **Credential storage backend**: Database-backed storage for access tokens and SSH keys with encryption
|
||||
- **Repository connection API**: REST endpoints for creating, listing, and deleting repository connections
|
||||
- **SSH key lifecycle**: Ed25519 key generation, public key retrieval, and deploy key registration
|
||||
- **Git operations**: Complete LocalGitOperations with credential-aware clone, fetch, and push
|
||||
- **Frontend repository UI**: Interface for connecting repositories and managing SSH keys
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `git-provider-adapter`: Unified interface for Git provider APIs (GitHub, GitLab)
|
||||
- `credential-storage`: Encrypted storage for access tokens and SSH keys
|
||||
- `repository-connection`: API for managing repository connections
|
||||
- `ssh-key-lifecycle`: SSH key generation and deploy key management
|
||||
- `git-operations`: Credential-aware Git operations (clone, fetch, push)
|
||||
|
||||
### Modified Capabilities
|
||||
- None (extends existing Git abstraction)
|
||||
|
||||
## Impact
|
||||
|
||||
- **apps/api/app/git/**: New provider adapters and completed operations
|
||||
- **apps/api/app/routers/repository_connections.py**: New router
|
||||
- **apps/api/app/models/repository_connection.py**: Enhanced model
|
||||
- **apps/api/app/schemas/repository_connection.py**: New schemas
|
||||
- **apps/web/src/pages/RepositoriesPage.tsx**: Enhanced UI
|
||||
- **apps/web/src/pages/RepositoryConnectionPage.tsx**: New page
|
||||
|
||||
## Dependencies
|
||||
|
||||
- FN-003: Tool Registry (manifest system)
|
||||
- FN-004: Backend Foundation (models, auth)
|
||||
- FN-009: Config & Secrets (encryption, credential storage)
|
||||
@@ -0,0 +1,30 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Credentials are stored encrypted
|
||||
The system SHALL store access tokens and SSH keys encrypted at rest.
|
||||
|
||||
#### Scenario: Store access token
|
||||
- **WHEN** a user saves an access token
|
||||
- **THEN** the system encrypts it with Fernet
|
||||
- **AND** stores the encrypted value in the database
|
||||
|
||||
#### Scenario: Retrieve access token
|
||||
- **WHEN** the system retrieves a credential for API calls
|
||||
- **THEN** it decrypts the value
|
||||
- **AND** returns the plaintext token
|
||||
|
||||
#### Scenario: List credentials without exposing values
|
||||
- **WHEN** a user lists their credentials
|
||||
- **THEN** the system returns metadata (name, provider, created_at)
|
||||
- **AND** masks the token value (showing only last 4 characters)
|
||||
|
||||
### Requirement: Credential storage supports multiple providers
|
||||
The system SHALL support storing credentials for different Git providers.
|
||||
|
||||
#### Scenario: Store GitHub token
|
||||
- **WHEN** a user adds a GitHub personal access token
|
||||
- **THEN** the system stores it with provider_type="github"
|
||||
|
||||
#### Scenario: Store GitLab token
|
||||
- **WHEN** a user adds a GitLab personal access token
|
||||
- **THEN** the system stores it with provider_type="gitlab"
|
||||
@@ -0,0 +1,29 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Git clone uses SSH credentials
|
||||
The system SHALL clone repositories using SSH keys.
|
||||
|
||||
#### Scenario: Clone repository
|
||||
- **WHEN** the system clones a repository
|
||||
- **THEN** it writes the SSH private key to a temporary file
|
||||
- **AND** sets GIT_SSH_COMMAND to use the key
|
||||
- **AND** executes git clone
|
||||
- **AND** cleans up the temporary key file
|
||||
|
||||
#### Scenario: Clone fails with invalid key
|
||||
- **WHEN** a clone operation fails due to authentication
|
||||
- **THEN** the system returns a clear error message
|
||||
- **AND** suggests checking deploy key permissions
|
||||
|
||||
### Requirement: Git fetch and push use credentials
|
||||
The system SHALL support fetch and push operations with SSH credentials.
|
||||
|
||||
#### Scenario: Fetch updates
|
||||
- **WHEN** the system fetches from a remote
|
||||
- **THEN** it uses the stored SSH key for authentication
|
||||
- **AND** returns the fetch result
|
||||
|
||||
#### Scenario: Push changes
|
||||
- **WHEN** the system pushes to a remote
|
||||
- **THEN** it uses the stored SSH key for authentication
|
||||
- **AND** returns the push result
|
||||
@@ -0,0 +1,35 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: GitHub adapter implements provider interface
|
||||
The system SHALL provide a GitHub adapter that implements the GitProvider interface.
|
||||
|
||||
#### Scenario: List repositories
|
||||
- **WHEN** the adapter lists repositories for an authenticated user
|
||||
- **THEN** it returns a list of repository objects with name, url, and default_branch
|
||||
|
||||
#### Scenario: Create deploy key
|
||||
- **WHEN** the adapter creates a deploy key for a repository
|
||||
- **THEN** it registers the SSH public key with GitHub
|
||||
- **AND** returns the key ID
|
||||
|
||||
#### Scenario: Validate connection
|
||||
- **WHEN** the adapter validates a token
|
||||
- **THEN** it verifies the token with GitHub API
|
||||
- **AND** returns user information
|
||||
|
||||
### Requirement: GitLab adapter implements provider interface
|
||||
The system SHALL provide a GitLab adapter that implements the GitProvider interface.
|
||||
|
||||
#### Scenario: List repositories
|
||||
- **WHEN** the adapter lists repositories for an authenticated user
|
||||
- **THEN** it returns a list of repository objects with name, url, and default_branch
|
||||
|
||||
#### Scenario: Create deploy key
|
||||
- **WHEN** the adapter creates a deploy key for a repository
|
||||
- **THEN** it registers the SSH public key with GitLab
|
||||
- **AND** returns the key ID
|
||||
|
||||
#### Scenario: Validate connection
|
||||
- **WHEN** the adapter validates a token
|
||||
- **THEN** it verifies the token with GitLab API
|
||||
- **AND** returns user information
|
||||
@@ -0,0 +1,42 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Repository connections can be created
|
||||
The system SHALL allow users to create connections to Git repositories.
|
||||
|
||||
#### Scenario: Connect GitHub repository
|
||||
- **WHEN** a user provides a GitHub repository URL and access token
|
||||
- **THEN** the system validates the URL and token
|
||||
- **AND** creates a RepositoryConnection record
|
||||
- **AND** generates an SSH key pair
|
||||
- **AND** registers the deploy key with GitHub
|
||||
|
||||
#### Scenario: Connect GitLab repository
|
||||
- **WHEN** a user provides a GitLab repository URL and access token
|
||||
- **THEN** the system validates the URL and token
|
||||
- **AND** creates a RepositoryConnection record
|
||||
- **AND** generates an SSH key pair
|
||||
- **AND** registers the deploy key with GitLab
|
||||
|
||||
#### Scenario: Reject invalid URL
|
||||
- **WHEN** a user provides an invalid repository URL
|
||||
- **THEN** the system returns a 400 error with validation message
|
||||
|
||||
### Requirement: Repository connections can be listed and retrieved
|
||||
The system SHALL allow users to list and view their repository connections.
|
||||
|
||||
#### Scenario: List connections
|
||||
- **WHEN** a user requests their repository connections
|
||||
- **THEN** the system returns a list with status and metadata
|
||||
|
||||
#### Scenario: Get connection details
|
||||
- **WHEN** a user requests a specific connection
|
||||
- **THEN** the system returns full details including SSH public key
|
||||
|
||||
### Requirement: Repository connections can be deleted
|
||||
The system SHALL allow users to delete repository connections.
|
||||
|
||||
#### Scenario: Delete connection
|
||||
- **WHEN** a user deletes a connection
|
||||
- **THEN** the system removes the deploy key from the provider
|
||||
- **AND** deletes the SSH key pair
|
||||
- **AND** marks the connection as deleted
|
||||
@@ -0,0 +1,28 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: SSH keys are generated per repository
|
||||
The system SHALL generate Ed25519 SSH key pairs for each repository connection.
|
||||
|
||||
#### Scenario: Generate key pair
|
||||
- **WHEN** a repository connection is created
|
||||
- **THEN** the system generates an Ed25519 key pair
|
||||
- **AND** stores the private key encrypted
|
||||
- **AND** returns the public key for deploy key registration
|
||||
|
||||
#### Scenario: Retrieve public key
|
||||
- **WHEN** a user requests the public key for a connection
|
||||
- **THEN** the system returns the SSH public key string
|
||||
|
||||
### Requirement: SSH keys support lifecycle operations
|
||||
The system SHALL support rotating and revoking SSH keys.
|
||||
|
||||
#### Scenario: Rotate key
|
||||
- **WHEN** a user rotates an SSH key
|
||||
- **THEN** the system generates a new key pair
|
||||
- **AND** updates the deploy key on the provider
|
||||
- **AND** deletes the old key pair
|
||||
|
||||
#### Scenario: Revoke key
|
||||
- **WHEN** a connection is deleted
|
||||
- **THEN** the system deletes the deploy key from the provider
|
||||
- **AND** securely deletes the local key pair
|
||||
@@ -0,0 +1,63 @@
|
||||
## 1. Provider Adapters
|
||||
|
||||
- [ ] 1.1 Implement GitHubAdapter in apps/api/app/git/providers/github.py
|
||||
- [ ] 1.2 Implement GitLabAdapter in apps/api/app/git/providers/gitlab.py
|
||||
- [ ] 1.3 Add provider factory in apps/api/app/git/providers/__init__.py
|
||||
- [ ] 1.4 Write tests for GitHubAdapter (mock API responses)
|
||||
- [ ] 1.5 Write tests for GitLabAdapter (mock API responses)
|
||||
|
||||
## 2. Credential Storage
|
||||
|
||||
- [ ] 2.1 Create CredentialStorage implementation in apps/api/app/git/credentials.py
|
||||
- [ ] 2.2 Add database model for GitCredential if needed
|
||||
- [ ] 2.3 Integrate Fernet encryption from app/encryption.py
|
||||
- [ ] 2.4 Add credential router in apps/api/app/routers/credentials.py
|
||||
- [ ] 2.5 Write tests for credential storage
|
||||
|
||||
## 3. SSH Key Lifecycle
|
||||
|
||||
- [ ] 3.1 Complete SshKeyLifecycle.generate_key_pair() with real Ed25519
|
||||
- [ ] 3.2 Add SSH key endpoints in apps/api/app/routers/ssh_keys.py
|
||||
- [ ] 3.3 Implement key rotation logic
|
||||
- [ ] 3.4 Write tests for SSH key generation
|
||||
|
||||
## 4. Repository Connection API
|
||||
|
||||
- [ ] 4.1 Create RepositoryConnection router in apps/api/app/routers/repository_connections.py
|
||||
- [ ] 4.2 Implement POST /api/v1/repository-connections endpoint
|
||||
- [ ] 4.3 Implement GET /api/v1/repository-connections endpoint
|
||||
- [ ] 4.4 Implement GET /api/v1/repository-connections/:id endpoint
|
||||
- [ ] 4.5 Implement DELETE /api/v1/repository-connections/:id endpoint
|
||||
- [ ] 4.6 Add validation for repository URLs and tokens
|
||||
- [ ] 4.7 Write tests for repository connection endpoints
|
||||
|
||||
## 5. Git Operations
|
||||
|
||||
- [ ] 5.1 Complete LocalGitOperations.clone() with SSH key
|
||||
- [ ] 5.2 Complete LocalGitOperations.fetch() with SSH key
|
||||
- [ ] 5.3 Complete LocalGitOperations.push() with SSH key
|
||||
- [ ] 5.4 Add error handling for auth failures
|
||||
- [ ] 5.5 Write tests for git operations
|
||||
|
||||
## 6. Frontend UI
|
||||
|
||||
- [ ] 6.1 Create RepositoryConnectionListPage.tsx
|
||||
- [ ] 6.2 Create RepositoryConnectionFormPage.tsx
|
||||
- [ ] 6.3 Add repository connection routes to router.tsx
|
||||
- [ ] 6.4 Add API client methods for repository connections
|
||||
- [ ] 6.5 Add types for repository connections
|
||||
|
||||
## 7. Documentation
|
||||
|
||||
- [ ] 7.1 Update docs/architecture.md with Git connection model
|
||||
- [ ] 7.2 Update docs/development.md with setup instructions
|
||||
- [ ] 7.3 Add provider setup guide (GitHub/GitLab tokens)
|
||||
|
||||
## 8. Testing & Verification
|
||||
|
||||
- [ ] 8.1 Run all backend tests (target: 90+)
|
||||
- [ ] 8.2 Run ruff linter
|
||||
- [ ] 8.3 Run mypy type checker
|
||||
- [ ] 8.4 Run frontend tests
|
||||
- [ ] 8.5 Verify API endpoints with manual testing
|
||||
- [ ] 8.6 Update project specsheet
|
||||
Reference in New Issue
Block a user