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
17 changes: 17 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# CipherStash Go Encryption SDK — example environment configuration.
#
# Copy this file to .env and fill in the values from your CipherStash workspace
# (`stash setup` writes them to cipherstash.toml / cipherstash.secret.toml, or
# you can set them here). Do not commit real credentials.

# Workspace CRN — identifies your workspace. Required for OIDC federation
# (WithOIDCFederation) when it is not supplied via WithCredentials.
export CS_WORKSPACE_CRN=crn:<region>.<provider>:<WORKSPACE_ID>

# Access key — authenticates the client to CipherStash.
export CS_CLIENT_ACCESS_KEY=

# Client ID and client key — the encryption key material. These are always
# required to encrypt and decrypt, regardless of the authentication strategy.
export CS_CLIENT_ID=
export CS_CLIENT_KEY=
41 changes: 40 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ jobs:
rust_target: x86_64-unknown-linux-gnu
libc: glibc

# Linux arm64 glibc (for Debian/Ubuntu/CentOS)
- platform: linux-arm64-gnu
os: ubuntu-22.04-arm
rust_target: aarch64-unknown-linux-gnu
libc: glibc

# Linux arm64 musl (for Alpine and static linking)
- platform: linux-arm64-musl
os: ubuntu-22.04-arm
Expand Down Expand Up @@ -126,7 +132,7 @@ jobs:
if: contains(matrix.rust_target, 'musl') && startsWith(matrix.os, 'ubuntu')
run: |
mkdir -p .cargo
echo '[target.x86_64-unknown-linux-musl]' >> .cargo/config.toml
echo '[target.${{ matrix.rust_target }}]' >> .cargo/config.toml
echo 'rustflags = ["-C", "target-feature=+crt-static"]' >> .cargo/config.toml

- name: Build library
Expand Down Expand Up @@ -165,6 +171,39 @@ jobs:
path: pkg/protect/libprotect_ffi_*.a
retention-days: 30

# Run Go unit tests against the freshly built native libraries
test-go:
name: Go Tests (${{ matrix.platform }})
needs: [build-native-libraries]
if: always() && needs.build-native-libraries.result == 'success'
strategy:
fail-fast: false
matrix:
include:
- platform: darwin-arm64
os: macos-14
- platform: linux-x64-gnu
os: ubuntu-22.04
runs-on: ${{ matrix.os }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Download library artifact
uses: actions/download-artifact@v4
with:
name: library-${{ matrix.platform }}
path: pkg/protect

- name: Run Go tests
run: go test ./pkg/protect/...

# Commit all generated libraries at once
commit-artifacts:
name: Commit All Generated Libraries
Expand Down
139 changes: 55 additions & 84 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -1,124 +1,95 @@
# Development Guide

This repo is a combination of the Protect.go module and the Protect.go FFI for the Rust C library which is used to create bindings for the `cipherstash-client` crate.
This repo combines the CipherStash Go Encryption SDK with the Rust C FFI library
that wraps the `cipherstash-client` crate.

## Architecture

The project consists of:

1. **Rust C Library** (`crates/protect-ffi-c/`) - A Rust library that exports C-compatible functions
2. **Go Package** (`pkg/protect/`) - Go bindings that wrap the C functions
3. **Examples** (`examples/`) - Usage examples showing how to use the library
1. **Rust C library** (`crates/protect-ffi-c/`) — a Rust crate that exports
C-compatible functions and generates the `protect_ffi.h` header.
2. **Go package** (`pkg/protect/`) — Go bindings that link the compiled static
library via cgo and expose an idiomatic Go API.
3. **Examples** (`examples/`) — runnable usage examples.

```
protectgo/
├── crates/
│ └── protect-ffi-c/ # Rust C FFI library
├── pkg/protect/ # Go package
├── pkg/protect/ # Go package + precompiled static libraries
├── examples/ # Usage examples
```

## Building
## Prerequisites

### Prerequisites
- Go (see the version in `go.mod`)
- A C toolchain (cgo is required)
- Rust (stable) — only needed if you are changing the Rust FFI layer

- [mise](https://mise.jdx.dev/) (handles Go and Rust versions automatically)
- CipherStash credentials and configuration
## How the native library is built

**Note**: mise will automatically install and manage the correct versions of Go (1.24.4) and Rust (nightly) for this project.
The Go package links a precompiled static library per platform, checked in under
`pkg/protect/` (for example `libprotect_ffi_darwin_arm64.a`). These are produced
in CI (see `.github/workflows/build.yml`) and committed back to the repo, so day
-to-day Go development does not require a Rust toolchain.

### Build Steps
To rebuild the native library locally after changing the Rust crate:

1. **Install mise** (if not already installed):
```bash
# macOS
brew install mise

# Linux
curl https://mise.run | sh
```
```bash
# Build for your host target
cargo build --release

2. **Install dependencies and tools**:
```bash
mise run install-deps
```
# Copy the resulting archive to the matching platform filename, e.g. on Apple
# Silicon:
cp target/release/libprotect_ffi.a pkg/protect/libprotect_ffi_darwin_arm64.a
```

3. **Build the project**:
```bash
mise run build
```
The header `pkg/protect/protect_ffi.h` is regenerated by the crate's build
script (cbindgen) as part of `cargo build`.

4. **Run tests**:
```bash
mise run test
```
## Building and testing the Go package

5. **Build and run example**:
```bash
mise run example
./bin/example
```
```bash
# Vet and build (links the platform static library via cgo)
go vet ./...
go build ./...

## Development
# Run the unit tests
go test ./...

### Project Structure
# Alpine Linux / musl targets
go test -tags=musl ./...

# Format
gofmt -w .
```
protectgo/
├── Cargo.toml # Rust workspace
├── go.mod # Go module
├── mise.toml # Task automation and tool management
├── Makefile # Legacy build automation (deprecated)
├── crates/
│ └── protect-ffi-c/ # Rust C FFI library
│ ├── Cargo.toml
│ ├── build.rs # Build script for header generation
│ ├── cbindgen.toml # Header generation config
│ └── src/
│ ├── lib.rs # Main FFI functions
│ └── encrypt_config.rs
├── pkg/
│ └── protect/ # Go package
│ └── protect.go # Go bindings
└── examples/
└── basic_usage.go # Usage example
```

### Building from Source

1. Clone the repository
2. Install mise (see Build Steps above)
3. Run `mise run install-deps` to install dependencies and tools
4. Run `mise run build` to build the library
5. Run `mise run test` to run tests

**Available tasks**: Run `mise tasks` to see all available tasks including formatting, linting, and cleanup commands.
## Running the example

### Memory Management

The Go bindings automatically handle memory management:
- C strings are automatically freed after use
- Client resources must be explicitly freed with `client.Free()`
- All returned data is copied to Go-managed memory
```bash
go run examples/basic_usage.go

## Testing
# Alpine Linux / musl
go run -tags=musl examples/basic_usage.go
```

Run the test suite with:
You will need CipherStash credentials in the environment (see the README's
authentication section) for the example to talk to the service.

```bash
mise run test
```
## Memory management

Additional development tasks:
The Go bindings handle memory management for you:

```bash
mise run fmt # Format code (Rust + Go)
mise run check # Run linting and quality checks
mise run clean # Clean build artifacts
```
- C strings passed into the FFI are freed after each call.
- All data returned from the FFI is copied into Go-managed memory before the C
allocation is freed.
- Client resources are released with `client.Close()` — `Client` implements
`io.Closer`, so `defer client.Close()` is the idiomatic pattern.

## Support

For support and questions:

- GitHub Issues: [protectgo/issues](https://github.com/cipherstash/protectgo/issues)
- CipherStash Documentation: [docs.cipherstash.com](https://docs.cipherstash.com)
- CipherStash Documentation: [docs.cipherstash.com](https://docs.cipherstash.com)
Loading