KeyEnvKeyEnv
SDKs & Integrations

.NET SDK

Official .NET SDK for KeyEnv.

.NET SDK

Official .NET SDK for KeyEnv with .NET 6.0+ support and async/await patterns.

Beta Release: This SDK is currently in development. APIs may change before the stable 1.0 release.

Installation

NuGet Package Manager

dotnet add package KeyEnv

Package Manager Console

Install-Package KeyEnv

PackageReference

Add to your .csproj:

<PackageReference Include="KeyEnv" Version="1.1.0" />

Quick Start

using KeyEnv;

var client = KeyEnvClient.Create(Environment.GetEnvironmentVariable("KEYENV_TOKEN")!);

// Load secrets into environment variables
int count = await client.LoadEnvAsync("your-project-id", "production");
Console.WriteLine($"Loaded {count} secrets");

// Access secrets
Console.WriteLine(Environment.GetEnvironmentVariable("DATABASE_URL"));

Initialize the Client

Simple Initialization

using KeyEnv;

var client = KeyEnvClient.Create(Environment.GetEnvironmentVariable("KEYENV_TOKEN")!);

With Options

For more control, use KeyEnvOptions:

using KeyEnv;

var client = KeyEnvClient.Create(new KeyEnvOptions
{
    Token = Environment.GetEnvironmentVariable("KEYENV_TOKEN")!,
    Timeout = TimeSpan.FromSeconds(60),
    CacheTtl = TimeSpan.FromMinutes(5)
});

Loading Secrets

Load into Environment Variables

The simplest way to use secrets in your application:

int count = await client.LoadEnvAsync("project-id", "production");
Console.WriteLine($"Loaded {count} secrets");

// Access via Environment.GetEnvironmentVariable
var dbUrl = Environment.GetEnvironmentVariable("DATABASE_URL");
var apiKey = Environment.GetEnvironmentVariable("API_KEY");

Get Secrets as Dictionary

Get secrets as a key-value dictionary:

var secrets = await client.GetSecretsAsDictionaryAsync("project-id", "production");
Console.WriteLine(secrets["DATABASE_URL"]);
Console.WriteLine(secrets["API_KEY"]);

Export as List

Get secrets with metadata:

var secrets = await client.GetSecretsAsync("project-id", "production");
foreach (var secret in secrets)
{
    Console.WriteLine($"{secret.Key}={secret.Value}");
}

Managing Secrets

Get a Single Secret

var secret = await client.GetSecretAsync("project-id", "production", "DATABASE_URL");
Console.WriteLine(secret.Value);
Console.WriteLine(secret.Description);

Set a Secret

Creates or updates a secret:

await client.SetSecretAsync("project-id", "production", "API_KEY", "sk_live_...");

// With description
await client.SetSecretAsync(
    "project-id",
    "production",
    "API_KEY",
    "sk_live_...",
    description: "Production API key"
);

Delete a Secret

await client.DeleteSecretAsync("project-id", "production", "OLD_KEY");

Bulk Operations

Bulk Import

Import multiple secrets at once:

using KeyEnv.Types;

var secrets = new List<SecretInput>
{
    new SecretInput { Key = "DATABASE_URL", Value = "postgres://localhost/mydb" },
    new SecretInput { Key = "REDIS_URL", Value = "redis://localhost:6379" },
    new SecretInput { Key = "API_KEY", Value = "sk_test_...", Description = "Test API key" }
};

var result = await client.BulkImportAsync(
    "project-id",
    "development",
    secrets,
    new BulkImportOptions { Overwrite = true }
);

Console.WriteLine($"Created: {result.Created}, Updated: {result.Updated}");

Generate .env File

var envContent = await client.GenerateEnvFileAsync("project-id", "production");
await File.WriteAllTextAsync(".env", envContent);

Projects & Environments

List Projects

var projects = await client.ListProjectsAsync();
foreach (var project in projects)
{
    Console.WriteLine($"{project.Name} ({project.Id})");
}

Get Project Details

var project = await client.GetProjectAsync("project-id");
Console.WriteLine($"Project: {project.Name}");
foreach (var env in project.Environments)
{
    Console.WriteLine($"  - {env.Name}");
}

List Environments

var environments = await client.ListEnvironmentsAsync("project-id");
foreach (var env in environments)
{
    Console.WriteLine(env.Name);
}

Environment Permissions

List Permissions

var permissions = await client.ListPermissionsAsync("project-id", "production");
foreach (var perm in permissions)
{
    Console.WriteLine($"{perm.UserEmail}: {perm.Role}");
}

Set Permission

await client.SetPermissionAsync("project-id", "production", "user-id", "write");

Get My Permissions

var response = await client.GetMyPermissionsAsync("project-id");
foreach (var perm in response.Permissions)
{
    Console.WriteLine($"{perm.EnvironmentName}: {perm.Role} (CanWrite: {perm.CanWrite})");
}
Console.WriteLine($"Is Team Admin: {response.IsTeamAdmin}");

Caching

Enable caching for better performance in high-throughput applications:

var client = KeyEnvClient.Create(new KeyEnvOptions
{
    Token = Environment.GetEnvironmentVariable("KEYENV_TOKEN")!,
    CacheTtl = TimeSpan.FromMinutes(5)
});

// First call fetches from API
await client.GetSecretsAsync("project-id", "production");

// Subsequent calls within TTL use cache
await client.GetSecretsAsync("project-id", "production");

// Clear cache when needed
client.ClearCache("project-id", "production");
client.ClearAllCache();

Error Handling

using KeyEnv;

try
{
    var secret = await client.GetSecretAsync("project-id", "production", "MISSING_KEY");
}
catch (KeyEnvException ex)
{
    Console.WriteLine($"Error {ex.StatusCode}: {ex.Message}");

    if (ex.IsNotFound)
    {
        Console.WriteLine("Secret not found");
    }
    else if (ex.IsUnauthorized)
    {
        Console.WriteLine("Invalid or expired token");
    }
    else if (ex.IsForbidden)
    {
        Console.WriteLine("Access denied");
    }
}

Cancellation Support

All async methods support cancellation tokens:

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));

try
{
    var secrets = await client.GetSecretsAsync("project-id", "production", cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation was cancelled");
}

IDisposable Pattern

The client implements IDisposable for proper resource cleanup:

using (var client = KeyEnvClient.Create(token))
{
    await client.LoadEnvAsync("project-id", "production");
    // Client is disposed when leaving the using block
}

// Or with using declaration (C# 8+)
using var client = KeyEnvClient.Create(token);
await client.LoadEnvAsync("project-id", "production");

API Reference

Constructor Options

OptionTypeRequiredDefaultDescription
TokenstringYes-Service token
TimeoutTimeSpan?No30sRequest timeout
CacheTtlTimeSpan?No0Cache TTL (0 disables)
BaseUrlstring?Nohttps://api.keyenv.devAPI base URL

Methods

MethodDescription
GetCurrentUserAsync()Get current user/token info
ValidateTokenAsync()Validate token and get user info
ListProjectsAsync()List all accessible projects
GetProjectAsync(projectId)Get project with environments
CreateProjectAsync(teamId, name)Create a new project
DeleteProjectAsync(projectId)Delete a project
ListEnvironmentsAsync(projectId)List environments in a project
CreateEnvironmentAsync(projectId, name)Create a new environment
DeleteEnvironmentAsync(projectId, env)Delete an environment
ListSecretsAsync(projectId, env)List secret keys (no values)
GetSecretsAsync(projectId, env)Export secrets with values
GetSecretsAsDictionaryAsync(projectId, env)Export as dictionary
GetSecretAsync(projectId, env, key)Get single secret
SetSecretAsync(projectId, env, key, value)Create or update secret
DeleteSecretAsync(projectId, env, key)Delete secret
BulkImportAsync(projectId, env, secrets)Bulk import secrets
LoadEnvAsync(projectId, env)Load secrets into environment
GenerateEnvFileAsync(projectId, env)Generate .env file content
GetSecretHistoryAsync(projectId, env, key)Get secret version history
ListPermissionsAsync(projectId, env)List permissions
SetPermissionAsync(projectId, env, userId, role)Set permission
DeletePermissionAsync(projectId, env, userId)Delete permission
GetMyPermissionsAsync(projectId)Get current user's permissions
GetProjectDefaultsAsync(projectId)Get default permissions
SetProjectDefaultsAsync(projectId, defaults)Set default permissions
ClearCache(projectId, environment)Clear specific cache
ClearAllCache()Clear all cached data

Examples

ASP.NET Core Web API

// Program.cs
using KeyEnv;

var builder = WebApplication.CreateBuilder(args);

// Load secrets at startup
var keyEnvToken = Environment.GetEnvironmentVariable("KEYENV_TOKEN");
if (!string.IsNullOrEmpty(keyEnvToken))
{
    using var client = KeyEnvClient.Create(keyEnvToken);
    await client.LoadEnvAsync(
        Environment.GetEnvironmentVariable("KEYENV_PROJECT")!,
        builder.Environment.EnvironmentName.ToLower()
    );
}

// Now secrets are available via Environment.GetEnvironmentVariable
builder.Services.AddControllers();
var app = builder.Build();

app.MapControllers();
app.Run();

ASP.NET Core with Dependency Injection

// Program.cs
using KeyEnv;

var builder = WebApplication.CreateBuilder(args);

// Register KeyEnvClient as a singleton
builder.Services.AddSingleton<KeyEnvClient>(sp =>
{
    return KeyEnvClient.Create(new KeyEnvOptions
    {
        Token = Environment.GetEnvironmentVariable("KEYENV_TOKEN")!,
        CacheTtl = TimeSpan.FromMinutes(5)
    });
});

var app = builder.Build();
app.Run();

// In a controller
[ApiController]
[Route("api/[controller]")]
public class SecretsController : ControllerBase
{
    private readonly KeyEnvClient _keyEnv;

    public SecretsController(KeyEnvClient keyEnv)
    {
        _keyEnv = keyEnv;
    }

    [HttpGet]
    public async Task<IActionResult> Get()
    {
        var secrets = await _keyEnv.GetSecretsAsDictionaryAsync("project-id", "production");
        return Ok(new { count = secrets.Count });
    }
}

Console Application

using KeyEnv;

class Program
{
    static async Task Main(string[] args)
    {
        var token = Environment.GetEnvironmentVariable("KEYENV_TOKEN")
            ?? throw new InvalidOperationException("KEYENV_TOKEN not set");

        using var client = KeyEnvClient.Create(token);

        // Load all secrets into environment
        var count = await client.LoadEnvAsync("my-project", "production");
        Console.WriteLine($"Loaded {count} secrets");

        // Use the secrets
        var dbUrl = Environment.GetEnvironmentVariable("DATABASE_URL");
        Console.WriteLine($"Database: {dbUrl}");
    }
}

Azure Functions

using KeyEnv;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services =>
    {
        services.AddSingleton<KeyEnvClient>(sp =>
        {
            return KeyEnvClient.Create(new KeyEnvOptions
            {
                Token = Environment.GetEnvironmentVariable("KEYENV_TOKEN")!,
                CacheTtl = TimeSpan.FromMinutes(5)
            });
        });
    })
    .Build();

// Load secrets at startup
using (var scope = host.Services.CreateScope())
{
    var client = scope.ServiceProvider.GetRequiredService<KeyEnvClient>();
    await client.LoadEnvAsync(
        Environment.GetEnvironmentVariable("KEYENV_PROJECT")!,
        "production"
    );
}

host.Run();

AWS Lambda with .NET

using Amazon.Lambda.Core;
using KeyEnv;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace MyLambda;

public class Function
{
    private static readonly KeyEnvClient _client;
    private static bool _secretsLoaded = false;

    static Function()
    {
        _client = KeyEnvClient.Create(new KeyEnvOptions
        {
            Token = Environment.GetEnvironmentVariable("KEYENV_TOKEN")!,
            CacheTtl = TimeSpan.FromMinutes(5)
        });
    }

    public async Task<string> FunctionHandler(object input, ILambdaContext context)
    {
        // Load secrets once per cold start
        if (!_secretsLoaded)
        {
            await _client.LoadEnvAsync(
                Environment.GetEnvironmentVariable("KEYENV_PROJECT")!,
                "production"
            );
            _secretsLoaded = true;
        }

        // Your handler logic here
        var apiKey = Environment.GetEnvironmentVariable("API_KEY");
        return $"Success with API key: {apiKey?.Substring(0, 10)}...";
    }
}

Background Service

using KeyEnv;

public class SecretsRefreshService : BackgroundService
{
    private readonly KeyEnvClient _client;
    private readonly ILogger<SecretsRefreshService> _logger;

    public SecretsRefreshService(KeyEnvClient client, ILogger<SecretsRefreshService> logger)
    {
        _client = client;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                var count = await _client.LoadEnvAsync("project-id", "production", stoppingToken);
                _logger.LogInformation("Refreshed {Count} secrets", count);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to refresh secrets");
            }

            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }
}

On this page