certynix

Official Ruby SDK for the Certynix Trust Infrastructure API.

Installation

Add to your Gemfile:

gem 'certynix'

Then run:

bundle install

Or install directly:

gem install certynix

Requires Ruby 3.1+.

Quick Start

require 'certynix'

client = Certynix::Client.new(ENV['CERTYNIX_API_KEY']) # cnx_live_sk_... or cnx_test_sk_...

# Register an asset by SHA-256 hash
asset = client.assets.register(
  hash_sha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
  filename: 'contract-2024.pdf'
)
puts "#{asset.id}#{asset.status}"
puts asset.is_first_registrant ? 'First registrant!' : 'Already registered'

# Public verification (no auth required)
result = client.verify.by_hash(asset.hash)
puts result.match ? 'Verified' : 'Not found'
puts result.first_registrant.organization_name

Authentication

# Production
client = Certynix::Client.new('cnx_live_sk_...')

# Sandbox (auto-detected from key prefix cnx_test_sk_)
client = Certynix::Client.new('cnx_test_sk_...')
# Automatically uses https://sandbox.certynix.com

# Custom options
client = Certynix::Client.new(
  'cnx_live_sk_...',
  base_url: 'https://api.staging.certynix.com',
  timeout: 30,
  max_retries: 3
)

Resources

Assets

# Register by hash
asset = client.assets.register(
  hash_sha256: 'abc123...',
  filename: 'document.pdf',
  source_url: 'https://example.com/document.pdf',
  metadata: { author: 'John Doe' }
)

# Register by URL (Certynix downloads and hashes)
asset = client.assets.register(
  source_url: 'https://example.com/document.pdf',
  filename: 'document.pdf'
)

# Register batch (up to 1,000 assets)
batch = client.assets.register_batch(
  assets: [
    { hash_sha256: 'abc...', filename: 'file1.pdf' },
    { hash_sha256: 'def...', filename: 'file2.pdf' }
  ]
)
puts "#{batch.batch_id}#{batch.status}"

# Get by ID
asset = client.assets.get('ast_abc123')

# List (Enumerable — all pages iterated automatically)
client.assets.list(limit: 50).each do |asset|
  puts "#{asset.id}#{asset.status}"
end

# List with filters
verified = client.assets.list(status: 'verified').to_a

# Lazy with limit
first_10 = client.assets.list.lazy.first(10)

# Delete (soft delete — history preserved)
client.assets.delete('ast_abc123')

Verification (public — no API key required)

# Verify by SHA-256 hash
result = client.verify.by_hash(
  'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
)
puts result.match ? 'Verified' : 'Not found'
puts result.first_registrant.organization_name

# Verify by asset ID
result = client.verify.by_asset_id('ast_abc123')

# Verify by URL
result = client.verify.by_url('https://example.com/document.pdf')

Webhooks

# Create webhook
webhook = client.webhooks.create(
  url: 'https://example.com/webhook',
  events: ['asset.created', 'asset.verified', 'exposure.alert.created']
)
# Store webhook.signing_secret securely — shown only once!

# List
client.webhooks.list.each do |webhook|
  puts "#{webhook.id}#{webhook.url}"
end

# Update
webhook = client.webhooks.update('wh_abc123', active: false)

# Delete
client.webhooks.delete('wh_abc123')

# Validate signature (in your webhook handler)
begin
  event = Certynix::Webhooks.validate_signature(
    raw_body: request.body.read,
    signature: request.headers['X-Certynix-Signature'],
    secret: ENV['CERTYNIX_WEBHOOK_SECRET']
  )
  puts event.type
  puts event.payload.inspect
rescue Certynix::WebhookSignatureError => e
  render plain: 'Invalid signature', status: :bad_request
rescue Certynix::WebhookReplayError => e
  render plain: 'Replay attack detected', status: :bad_request
end

Exposure Alerts

# List active alerts
client.alerts.list(resolved: false).each do |alert|
  puts "[#{alert.severity}] #{alert.description}"
end

API Keys

# Create — value shown only once!
key = client.api_keys.create(name: 'Production App')
puts key.key_value  # cnx_live_sk_... — store immediately!

# List
client.api_keys.list.each do |key|
  puts "#{key.name}#{key.prefix}"
end

# Revoke
client.api_keys.revoke('key_abc123')

Audit Logs

client.audit_logs.list(limit: 50).each do |log|
  puts "#{log.action} by #{log.actor_id} at #{log.created_at}"
end

# Filter by action
client.audit_logs.list(action: 'asset.created').each do |log|
  puts log.resource_id
end

Trust Score

score = client.trust_score.get
puts "Score: #{score.score}/100"
puts "Identity: #{score.components.identity}"
puts "Security: #{score.components.security}"
puts "Behavior: #{score.components.behavior}"
puts "Assets:   #{score.components.assets}"

score.penalties.each do |penalty|
  puts "Penalty: #{penalty.description} (-#{penalty.points})"
end

Error Handling

require 'certynix'

begin
  asset = client.assets.get('ast_notfound')
rescue Certynix::NotFoundError => e
  puts "Not found: #{e.message}"
  puts "Code: #{e.code}"
  puts "Request ID: #{e.request_id}"
rescue Certynix::RateLimitError => e
  puts "Rate limited. Retry after: #{e.retry_after}s"
rescue Certynix::AuthenticationError => e
  puts "Invalid API key"
rescue Certynix::ServerError => e
  puts "Server error: #{e.message}"
rescue Certynix::NetworkError => e
  puts "Network error: #{e.message}"
end

Pagination

All list methods return a Paginator that includes Ruby's Enumerable:

# Iterate all pages automatically
client.assets.list(limit: 100).each do |asset|
  puts asset.id
end

# Collect all into array
assets = client.assets.list.to_a

# Lazy enumeration (stops fetching when you stop iterating)
first_10 = client.assets.list.lazy.first(10)

# Map, select, reduce
verified_ids = client.assets.list
  .select { |a| a.status == 'verified' }
  .map(&:id)

Retry Policy

The SDK automatically retries on transient errors (via faraday-retry):

Scenario Retried
429 Too Many Requests Yes — respects Retry-After
500, 502, 503, 504 Yes — exponential backoff + jitter
Network errors Yes
400, 401, 403, 404, 409 No

Default: 3 retries, max 60s backoff.

Webhook Signature Validation

# Rails example
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def certynix
    event = Certynix::Webhooks.validate_signature(
      raw_body: request.body.read,
      signature: request.headers['X-Certynix-Signature'],
      secret: Rails.application.credentials.certynix_webhook_secret
    )

    case event.type
    when 'asset.created'
      AssetCreatedJob.perform_later(event.payload)
    when 'asset.verified'
      AssetVerifiedJob.perform_later(event.payload)
    when 'exposure.alert.created'
      AlertCreatedJob.perform_later(event.payload)
    end

    head :ok
  rescue Certynix::WebhookSignatureError, Certynix::WebhookReplayError
    head :bad_request
  end
end

Testing

bundle install
bundle exec rspec

License

MIT