# Architecture
This document describes the architecture of `corteq-onepassword`, a Rust wrapper around the 1Password SDK.
## High-Level Overview
```mermaid
flowchart TB
subgraph "User Application"
App[Application Code]
end
subgraph "corteq-onepassword"
Client[OnePassword Client]
Builder[OnePasswordBuilder]
Secret[SecretReference / SecretMap]
Error[Error Types]
subgraph "FFI Layer"
Bindings[SdkClient]
Loader[NativeLibrary]
Protocol[JSON Protocol]
Types[UniFFI Types]
end
end
subgraph "Native Library"
SDK[libop_uniffi_core.so/.dylib]
end
subgraph "1Password Cloud"
API[1Password API]
end
App --> Client
Client --> Bindings
Bindings --> Loader
Loader --> SDK
SDK --> API
```
## Module Structure
```mermaid
graph TD
subgraph "Public API (lib.rs)"
OnePassword
OnePasswordBuilder
Error
SecretReference
SecretMap
ExposeSecret[ExposeSecret re-export]
SecretString[SecretString re-export]
end
subgraph "Internal Modules"
client.rs --> ffi/mod.rs
client.rs --> secret.rs
client.rs --> error.rs
ffi/mod.rs --> ffi/bindings.rs
ffi/mod.rs --> ffi/loader.rs
ffi/bindings.rs --> ffi/protocol.rs
ffi/bindings.rs --> ffi/uniffi_types.rs
ffi/loader.rs --> ffi/uniffi_types.rs
end
```
## Module Responsibilities
| **client** | `src/client.rs` | Public API, builder pattern, token validation |
| **error** | `src/error.rs` | Error types with security-safe messages |
| **secret** | `src/secret.rs` | Reference parsing, secret collections |
| **ffi** | `src/ffi/mod.rs` | FFI module root |
| **bindings** | `src/ffi/bindings.rs` | Safe Rust wrapper over raw FFI |
| **loader** | `src/ffi/loader.rs` | Dynamic library loading |
| **protocol** | `src/ffi/protocol.rs` | JSON serialization for SDK calls |
| **uniffi_types** | `src/ffi/uniffi_types.rs` | C ABI types for UniFFI |
## Data Flow: Resolving a Secret
```mermaid
sequenceDiagram
participant App as Application
participant Client as OnePassword
participant Bindings as SdkClient
participant Loader as NativeLibrary
participant SDK as libop_uniffi_core
participant Cloud as 1Password API
App->>Client: secret("op://vault/item/field")
Client->>Client: Parse SecretReference
Client->>Client: Lock async Mutex
Client->>Bindings: resolve_secret(reference)
Bindings->>Bindings: Build SdkInvocation JSON
Bindings->>Loader: invoke_sync(json)
Loader->>SDK: FFI call with RustBuffer
SDK->>Cloud: HTTPS request
Cloud-->>SDK: Encrypted response
SDK-->>Loader: RustBuffer with result
Loader-->>Bindings: JSON string
Bindings->>Bindings: Parse response
Bindings-->>Client: SecretString
Client-->>App: SecretString
```
## Connection Flow
```mermaid
sequenceDiagram
participant App as Application
participant Builder as OnePasswordBuilder
participant Client as OnePassword
participant Loader as loader.rs
participant Bindings as SdkClient
App->>Builder: from_env() / from_token()
Builder->>Builder: Validate token format
App->>Builder: .integration(name, version)
App->>Builder: .connect()
Builder->>Loader: load_library()
Loader->>Loader: Find bundled library
Loader->>Loader: dlopen() native library
Loader->>Loader: Resolve FFI symbols
Loader-->>Builder: Arc<NativeLibrary>
Builder->>Bindings: SdkClient::init(library, token, ...)
Bindings->>Bindings: Serialize InitClientParams
Bindings->>Loader: init_client(json)
Loader-->>Bindings: client_id
Bindings-->>Builder: SdkClient
Builder->>Client: Wrap in Arc<Mutex<>>
Builder-->>App: OnePassword
```
## FFI Layer Architecture
```mermaid
flowchart TB
subgraph "Rust (Safe)"
SdkClient[SdkClient<br/>bindings.rs]
Protocol[SdkInvocation<br/>protocol.rs]
end
subgraph "Rust (Unsafe Boundary)"
NativeLib[NativeLibrary<br/>loader.rs]
Types[RustBuffer / RustCallStatus<br/>uniffi_types.rs]
end
subgraph "Native (C ABI)"
InitClient[uniffi_op_uniffi_core_fn_func_init_client]
Invoke[uniffi_op_uniffi_core_fn_func_invoke]
Release[uniffi_op_uniffi_core_fn_func_release_client]
AllocBuf[ffi_op_uniffi_core_rustbuffer_alloc]
FreeBuf[ffi_op_uniffi_core_rustbuffer_free]
end
SdkClient --> Protocol
SdkClient --> NativeLib
NativeLib --> Types
NativeLib -.->|dlsym| InitClient
NativeLib -.->|dlsym| Invoke
NativeLib -.->|dlsym| Release
NativeLib -.->|dlsym| AllocBuf
NativeLib -.->|dlsym| FreeBuf
```
## Native Library Loading
```mermaid
flowchart TD
Start([load_library called])
Start --> CheckEnv{ONEPASSWORD_LIB_PATH<br/>set?}
CheckEnv -->|Yes| ValidateEnv{Path exists &<br/>correct filename?}
ValidateEnv -->|Yes| LoadCustom[Load from custom path]
ValidateEnv -->|No| Error1[Error: Invalid path]
CheckEnv -->|No| DetectPlatform[Detect OS + Arch]
DetectPlatform --> BuildPath[Build bundled path:<br/>src/libs/{platform}/libop_uniffi_core.*]
BuildPath --> CheckBundled{Bundled lib<br/>exists?}
CheckBundled -->|Yes| LoadBundled[Load from bundled path]
CheckBundled -->|No| Error2[Error: Library not found]
LoadCustom --> ResolveSymbols
LoadBundled --> ResolveSymbols
ResolveSymbols[Resolve 5 FFI symbols]
ResolveSymbols --> Success([Return Arc<NativeLibrary>])
```
## Thread Safety Model
```mermaid
flowchart LR
subgraph "Thread 1"
T1[Task 1]
end
subgraph "Thread 2"
T2[Task 2]
end
subgraph "Thread 3"
T3[Task 3]
end
subgraph "OnePassword Client"
Arc[Arc<OnePassword>]
Mutex[Mutex<SdkClient>]
Client[SdkClient]
end
T1 --> Arc
T2 --> Arc
T3 --> Arc
Arc --> Mutex
Mutex -->|serialized access| Client
```
The client uses `Arc<Mutex<SdkClient>>` to ensure:
- **Arc**: Multiple ownership across tasks/threads
- **Mutex**: Serialized access to the underlying SDK client
- **Send + Sync**: Safe to share across thread boundaries
## Security Architecture
```mermaid
flowchart TB
subgraph "Sensitive Data Handling"
Token[Service Account Token]
Secrets[Retrieved Secrets]
end
subgraph "Protection Mechanisms"
SecretString[SecretString wrapper]
Zeroize[Automatic zeroization]
NoDebug[Redacted Debug impls]
NoError[No secrets in errors]
end
Token --> SecretString
Secrets --> SecretString
SecretString --> Zeroize
SecretString --> NoDebug
SecretString --> NoError
```
### Security Guarantees
| Token exposure | Wrapped in `SecretString`, zeroized on drop |
| Secret logging | `Debug` impls show `[REDACTED]` |
| Error messages | Never contain token or secret values |
| Memory safety | Rust ownership + bounds checking |
| Concurrent access | `Mutex` serialization |
## Error Handling
```mermaid
flowchart TD
subgraph "Error Sources"
EnvVar[Missing env var]
TokenFmt[Invalid token format]
LibLoad[Library load failure]
Auth[Authentication failure]
Network[Network issues]
NotFound[Secret not found]
Access[Access denied]
end
subgraph "Error Types"
MissingAuthToken
InvalidToken
LibraryLoadError
AuthenticationFailed
NetworkError
SecretNotFound
AccessDenied
SdkError
end
EnvVar --> MissingAuthToken
TokenFmt --> InvalidToken
LibLoad --> LibraryLoadError
Auth --> AuthenticationFailed
Network --> NetworkError
NotFound --> SecretNotFound
Access --> AccessDenied
```
## Platform Support
```mermaid
flowchart LR
subgraph "Supported"
Linux_x64[Linux x86_64]
Linux_arm[Linux aarch64]
macOS_x64[macOS x86_64]
macOS_arm[macOS aarch64]
end
subgraph "Native Libraries"
so_x64[libop_uniffi_core.so<br/>linux-x86_64]
so_arm[libop_uniffi_core.so<br/>linux-aarch64]
dylib_x64[libop_uniffi_core.dylib<br/>macos-x86_64]
dylib_arm[libop_uniffi_core.dylib<br/>macos-aarch64]
end
Linux_x64 --> so_x64
Linux_arm --> so_arm
macOS_x64 --> dylib_x64
macOS_arm --> dylib_arm
```
## File Layout
```
src/
├── lib.rs # Crate root, public exports
├── client.rs # OnePassword, OnePasswordBuilder
├── error.rs # Error enum, Result type alias
├── secret.rs # SecretReference, SecretMap
└── ffi/
├── mod.rs # FFI module root
├── bindings.rs # SdkClient - safe FFI wrapper
├── loader.rs # NativeLibrary - dynamic loading
├── protocol.rs # JSON serialization types
└── uniffi_types.rs # RustBuffer, RustCallStatus
src/libs/ # Bundled native libraries
├── linux-x86_64/
├── linux-aarch64/
├── macos-x86_64/
└── macos-aarch64/
```
## Key Design Decisions
### 1. Builder Pattern
Separates configuration from connection, allowing validation before network calls.
### 2. Async-First with Blocking Option
Primary API is async (`connect()`), with optional `connect_blocking()` for sync contexts.
### 3. FFI Isolation
All unsafe FFI code is contained in the `ffi/` module, with safe wrappers exposed to the rest of the crate.
### 4. SecretString Everywhere
All sensitive data uses `SecretString` from the `secrecy` crate for automatic zeroization.
### 5. Bundled Libraries
Native SDK libraries are bundled in the crate to simplify deployment.