Local Development
By default, Cloacina does not require package signatures, making local development straightforward. This guide covers security considerations for development workflows.
The default security configuration disables signature requirements:
use cloacina::security::SecurityConfig;
// Default: no signatures required
let config = SecurityConfig::default();
assert!(!config.require_signatures);
// Explicit spelling of the same thing:
let config = SecurityConfig::development();
This allows you to:
- Build, pack, and upload packages without signing
- Iterate quickly during development
- Run tests without key management overhead
For day-to-day development, nothing security-related is needed: run your
server without --require-signatures (the default) and use the
normal authoring loop —
cloacinactl package new my-workflow --lang rust
cloacinactl package validate my-workflow
cloacinactl package pack my-workflow
cloacinactl package upload my-workflow/my-workflow.cloacina
Uploads are accepted unverified; signature checking only happens on
servers started with --require-signatures (see
Require signed packages).
Note:
cloacinactl package pack --signfails hard — CLI-driven signing is unimplemented (I-0103). Everything below exercises the library signing API from Rust code, which is how signing is done today.
If you want to test the signing machinery locally, use the library API from a small Rust program or test:
use cloacina::crypto::generate_signing_keypair;
let keypair = generate_signing_keypair();
println!("Public key: {} bytes", keypair.public_key.len());
println!("Fingerprint: {}", keypair.fingerprint);
// Save keys for later use
std::fs::write("dev-key.pub", &keypair.public_key)?;
std::fs::write("dev-key.priv", &keypair.private_key)?;
use std::path::Path;
use cloacina::security::{DbPackageSigner, PackageSigner, DetachedSignature};
let signer = DbPackageSigner::new(dal);
let signature = signer.sign_package_with_raw_key(
Path::new("./my-workflow/my-workflow.cloacina"),
&private_key,
&public_key,
)?;
// Write detached signature sidecar
let detached = DetachedSignature::from_signature_info(&signature);
detached.write_to_file(Path::new("./my-workflow/my-workflow.cloacina.sig"))?;
use std::path::Path;
use cloacina::security::verify_package_offline;
let result = verify_package_offline(
Path::new("./my-workflow/my-workflow.cloacina"),
Path::new("./my-workflow/my-workflow.cloacina.sig"),
&public_key,
)?;
println!("Verified! Hash: {}", result.package_hash);
To test that verification correctly rejects invalid packages:
#[test]
fn test_tampered_package_fails() {
// Sign a package
let signature = sign_package(&package_path, &private_key, &public_key)?;
// Tamper with it
let mut content = std::fs::read(&package_path)?;
content[0] ^= 0xFF;
std::fs::write(&package_path, &content)?;
// Verification should fail
let result = verify_package_offline(&package_path, &sig_path, &public_key);
assert!(matches!(result, Err(VerificationError::TamperedPackage { .. })));
}
For CI environments:
-
Development/test jobs: run the server without
--require-signatures(leaveCLOACINA_REQUIRE_SIGNATURESunset) and upload unsigned packages. -
Staging/production: start the server with
--require-signaturesand--verification-org-id(both required together — the server fails fast if only one is set), and produce signatures in your pipeline with a small Rust helper built oncloacina::security::package_signer(there is nocloacinactlsigning command yet):# .github/workflows/release.yml (sketch) - name: Build and pack run: | cloacinactl package build my-workflow cloacinactl package pack my-workflow - name: Sign package env: SIGNING_KEY: ${{ secrets.SIGNING_PRIVATE_KEY }} run: | # Your own helper binary wrapping sign_package_with_raw_key + # DetachedSignature — produces my-workflow.cloacina.sig ./tools/sign-package my-workflow/my-workflow.cloacina
Before deploying to production:
- Start the server with
--require-signaturesand--verification-org-id - Generate and securely store signing keys
- Register trusted public keys against the verification org
- Store the 32-byte key-encryption master key in a secrets manager
- Set up audit log monitoring (
package.load.*,verification.*,key.*events) - Test verification in staging environment
- Document key rotation procedures
The package has no signature. Either:
- Sign the package before uploading
- Disable signature requirements for development
The signing key is not trusted by this organization:
- Trust the public key:
key_manager.trust_public_key(org_id, &pub_key, None).await? - Or check if there’s a trust ACL issue
The package content has changed since signing:
- Re-sign the package after any modifications
- Verify you’re loading the correct file
Cryptographic verification failed:
- Ensure the correct public key is being used
- Check for data corruption in the signature file