# CLAUDE.md - hcfs-shared

Shared types and utilities used by both client and server components.

## Build & Test

```bash
cargo build -p hcfs-shared
cargo test -p hcfs-shared
```

## Key Files

| File | Purpose |
|------|---------|
| `src/lib.rs` | Module exports |
| `src/network.rs` | API types, Manifest, response enums |
| `src/http.rs` | HTTP client config, header parsing |
| `src/utils.rs` | Serialization helpers |

## Module: network.rs

### Response Types

```rust
// Generic wrapper for all API responses
pub enum NetworkResponse<T> {
    Success(T),
    Conflict(ConflictResponse),
    Error(ErrorResponse),
}

// Type aliases for each endpoint
pub type DownloadResponse = NetworkResponse<DownloadResult>;
pub type UploadResponse = NetworkResponse<UploadResult>;
pub type DeleteResponse = NetworkResponse<DeleteResult>;
pub type GetStateResponse = NetworkResponse<GetStateResult>;
```

### Manifest (Upload Request)

```rust
#[derive(Deserialize, Serialize)]
pub struct Manifest {
    pub user_id: String,
    pub version: String,
    pub file_id: String,              // Hex-encoded path_hash
    pub ciphertext_hash: String,      // BLAKE3 of ciphertext
    pub size_bytes: u64,
    pub timestamp: i64,
    pub signature: Vec<u8>,           // Ed25519 signature (64 bytes)
    pub signing_key: [u8; 32],        // Public key
    pub path_hash: [u8; 32],
    pub salted_hash: [u8; 32],        // BLAKE3(user_id || plaintext)
    pub revision_seq: u64,            // Monotonic sequence
    pub base_revision_id: Option<[u8; 32]>,  // For OCC
    pub encrypted_path: Vec<u8>,      // For path recovery
}
```

### Manifest Verification

```rust
impl Manifest {
    /// Generate ToS text that was signed
    pub fn generate_text(hash: &str) -> String {
        format!("I here by declare that the file with hash {} ...", hash)
    }

    /// Verify signature only (streaming - can't check ciphertext)
    pub fn verify_signature(&self) -> Result<()>;

    /// Verify signature AND ciphertext hash (buffered)
    pub async fn verify_request(&self, ciphertext: &[u8]) -> Result<()>;
}
```

### RemoteFileEntry

```rust
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemoteFileEntry {
    pub path_hash: [u8; 32],
    pub salted_hash: [u8; 32],
    pub size_bytes: u64,
    pub revision_seq: u64,
    pub revision_id: [u8; 32],
    pub encrypted_path: Vec<u8>,
}
```

### Result Types

```rust
pub struct UploadResult {
    pub upload_id: String,      // Arion CID
    pub file_id: String,
    pub timestamp: i64,
    pub revision_id: [u8; 32],  // Server-assigned
}

pub struct DownloadResult {
    pub revision_seq: u64,
    pub revision_id: [u8; 32],
    pub size_bytes: u64,
}

pub struct DeleteResult {
    pub status: String,
    pub file_id: String,
    pub user_id: String,
}

pub struct GetStateResult {
    pub user_id: String,
    pub files: Vec<RemoteFileEntry>,
    pub total_count: u64,
    pub has_more: bool,
    pub offset: u32,
    pub limit: u32,
}
```

### Error Types

```rust
pub struct ErrorResponse {
    pub error: String,    // Code: "unauthorized", "not_found", etc.
    pub message: String,  // Human-readable
}

pub struct ConflictResponse {
    pub error: String,
    pub message: String,
    pub current_revision_id: [u8; 32],
    pub current_revision_seq: u64,
}
```

## Module: http.rs

### HttpClientConfig

```rust
pub struct HttpClientConfig {
    pub timeout: Duration,              // Default: 5 minutes
    pub connect_timeout: Duration,      // Default: 30 seconds
    pub accept_invalid_certs: bool,     // Default: false
}

// Builder pattern
let config = HttpClientConfig::new()
    .with_timeout(Duration::from_secs(600))
    .with_accept_invalid_certs(true);
```

### Header Utilities

```rust
/// Parse u64 from header (e.g., Content-Length)
pub fn parse_header_u64(headers: &HeaderMap, name: &str) -> Option<u64>;

/// Parse 32-byte array from hex header (e.g., X-Revision-Id)
pub fn parse_header_bytes32(headers: &HeaderMap, name: &str) -> Option<[u8; 32]>;

/// Build auth headers
pub fn build_auth_headers(api_key: Option<&str>, bearer: Option<&str>) -> HeaderMap;
```

## Module: utils.rs

### Hex Key Map Serialization

```rust
// For HashMap<[u8; 32], T> serialization with hex string keys
pub mod hex_key_map {
    pub fn serialize<S>(map: &HashMap<[u8; 32], V>, serializer: S) -> Result<...>;
    pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<[u8; 32], V>, ...>;
}

// Usage in structs:
#[serde(with = "hcfs_shared::utils::hex_key_map")]
pub files: HashMap<[u8; 32], FileMetadata>,
```

## Common Tasks

### Adding a new API response type

1. Define result struct:
   ```rust
   #[derive(Clone, Debug, Deserialize, Serialize)]
   pub struct NewResult {
       pub field: String,
   }
   ```

2. Create type alias:
   ```rust
   pub type NewResponse = NetworkResponse<NewResult>;
   ```

3. Use in server handler and client

### Adding a new Manifest field

1. Add field with `#[serde(default)]` for backward compatibility:
   ```rust
   #[serde(default)]
   pub new_field: Option<String>,
   ```

2. Update client to populate field
3. Update server to read/validate field

### Adding a new header parser

1. Add function in `http.rs`:
   ```rust
   pub fn parse_header_new_type(headers: &HeaderMap, name: &str) -> Option<NewType> {
       let value = headers.get(name)?.to_str().ok()?;
       // Parse and return
   }
   ```

2. Add tests

## Testing

```bash
cargo test -p hcfs-shared

# Run specific test
cargo test -p hcfs-shared test_parse_header_u64
```

## Dependencies

- `serde` / `serde_json` - Serialization
- `ed25519-dalek` - Signature verification
- `blake3` - Hashing
- `reqwest` - HTTP types (HeaderMap)
- `hex` - Hex encoding/decoding
