{"id":34582927,"url":"https://github.com/replane-dev/replane-dotnet","last_synced_at":"2026-05-28T10:32:44.795Z","repository":{"id":329779978,"uuid":"1120114130","full_name":"replane-dev/replane-dotnet","owner":"replane-dev","description":"Dynamic configuration for C# and .NET","archived":false,"fork":false,"pushed_at":"2026-05-11T02:44:15.000Z","size":137,"stargazers_count":1,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T04:38:49.444Z","etag":null,"topics":["asp-net-core","csharp","dotnet","dynamic-config","dynamic-log-level","environment-variables","feature-flags","kill-switches","operational-tuning","remote-config"],"latest_commit_sha":null,"homepage":"https://replane.dev","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/replane-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-20T14:17:00.000Z","updated_at":"2026-05-04T14:13:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/replane-dev/replane-dotnet","commit_stats":null,"previous_names":["replane-dev/replane-dotnet"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/replane-dev/replane-dotnet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replane-dev%2Freplane-dotnet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replane-dev%2Freplane-dotnet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replane-dev%2Freplane-dotnet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replane-dev%2Freplane-dotnet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/replane-dev","download_url":"https://codeload.github.com/replane-dev/replane-dotnet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replane-dev%2Freplane-dotnet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33605378,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asp-net-core","csharp","dotnet","dynamic-config","dynamic-log-level","environment-variables","feature-flags","kill-switches","operational-tuning","remote-config"],"created_at":"2025-12-24T10:21:45.846Z","updated_at":"2026-05-28T10:32:44.785Z","avatar_url":"https://github.com/replane-dev.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eReplane .NET SDK\u003c/h1\u003e\n\u003cp align=\"center\"\u003eDynamic configuration for .NET applications.\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.nuget.org/packages/Replane\"\u003e\u003cimg src=\"https://img.shields.io/nuget/v/Replane\" alt=\"NuGet\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/replane-dev/replane-dotnet/actions\"\u003e\u003cimg src=\"https://github.com/replane-dev/replane-dotnet/actions/workflows/publish.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/replane-dev/replane-dotnet/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/replane-dev/replane-dotnet\" alt=\"License\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/orgs/replane-dev/discussions\"\u003e\u003cimg src=\"https://img.shields.io/badge/discussions-join-blue?logo=github\" alt=\"Community\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/replane-dev/replane/main/public/replane-window-screenshot-dark-v1.png\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/replane-dev/replane/main/public/replane-window-screenshot-light-with-border-v2.jpg\"\u003e\n    \u003cimg alt=\"Replane Screenshot\" src=\"https://raw.githubusercontent.com/replane-dev/replane/main/public/replane-window-screenshot-light-with-border-v2.jpg\"\u003e\n\u003c/picture\u003e\n\n[Replane](https://github.com/replane-dev/replane) is a dynamic configuration manager that lets you tweak your software without running scripts or building your own admin panel. Store feature flags, rate limits, UI text, log level, rollout percentage, and more. Delegate editing to teammates and share config across services. No redeploys needed.\n\n## Why Dynamic Configuration?\n\n- **Feature flags** – toggle features, run A/B tests, roll out to user segments\n- **Operational tuning** – adjust limits, TTLs, and timeouts without redeploying\n- **Per-environment settings** – different values for production, staging, dev\n- **Incident response** – instantly revert to a known-good version\n- **Cross-service configuration** – share settings with realtime sync\n- **Non-engineer access** – safe editing with schema validation\n\n## Installation\n\n```bash\ndotnet add package Replane\n```\n\n## Quick Start\n\n```csharp\nusing Replane;\n\n// Create client and connect\nawait using var replane = new ReplaneClient();\n\nawait replane.ConnectAsync(new ConnectOptions\n{\n    BaseUrl = \"https://replane.example.com\",\n    SdkKey = \"your-sdk-key\"\n});\n\n// Get a config value\nvar featureEnabled = replane.Get\u003cbool\u003e(\"feature-enabled\");\nvar maxItems = replane.Get\u003cint\u003e(\"max-items\", defaultValue: 100);\n```\n\n## Features\n\n- **Real-time updates** via Server-Sent Events (SSE)\n- **Client-side evaluation** - context never leaves your application\n- **Gradual rollouts** with percentage-based segmentation\n- **Override rules** with flexible conditions\n- **Type-safe** configuration access\n- **Async/await** support throughout\n- **In-memory test client** for unit testing\n\n## Usage\n\n### Basic Configuration Access\n\n```csharp\n// Get typed config values\nvar enabled = replane.Get\u003cbool\u003e(\"feature-enabled\");\nvar limit = replane.Get\u003cint\u003e(\"rate-limit\");\nvar apiKey = replane.Get\u003cstring\u003e(\"api-key\");\n\n// With default values\nvar timeout = replane.Get\u003cint\u003e(\"timeout-ms\", defaultValue: 5000);\n```\n\n### Complex Types\n\nConfigs can store complex objects that are deserialized on demand:\n\n```csharp\n// Define your config type\npublic record ThemeConfig\n{\n    public bool DarkMode { get; init; }\n    public string PrimaryColor { get; init; } = \"\";\n    public int FontSize { get; init; }\n}\n\n// Get complex config\nvar theme = replane.Get\u003cThemeConfig\u003e(\"theme\");\nConsole.WriteLine($\"Dark mode: {theme.DarkMode}, Color: {theme.PrimaryColor}\");\n\n// Works with overrides too - different themes for different users\nvar userTheme = replane.Get\u003cThemeConfig\u003e(\"theme\", new ReplaneContext { [\"plan\"] = \"premium\" });\n```\n\n### Context-Based Overrides\n\nEvaluate configs based on user context:\n\n```csharp\n// Create context for override evaluation\nvar context = new ReplaneContext\n{\n    [\"user_id\"] = \"user-123\",\n    [\"plan\"] = \"premium\",\n    [\"region\"] = \"us-east\"\n};\n\n// Get config with context\nvar premiumFeature = replane.Get\u003cbool\u003e(\"premium-feature\", context);\n```\n\n### Default Context\n\nSet default context that's merged with per-call context:\n\n```csharp\nvar replane = new ReplaneClient(new ReplaneClientOptions\n{\n    BaseUrl = \"https://your-server.com\",\n    SdkKey = \"your-key\",\n    Context = new ReplaneContext\n    {\n        [\"app_version\"] = \"2.0.0\",\n        [\"platform\"] = \"ios\"\n    }\n});\n```\n\n### Real-time Updates\n\nSubscribe to config changes using the `ConfigChanged` event:\n\n```csharp\n// Subscribe to all config changes\nreplane.ConfigChanged += (sender, e) =\u003e\n{\n    Console.WriteLine($\"Config '{e.ConfigName}' updated\");\n};\n\n// Get typed value from the event\nreplane.ConfigChanged += (sender, e) =\u003e\n{\n    if (e.ConfigName == \"feature-flag\")\n    {\n        var enabled = e.GetValue\u003cbool\u003e();\n        Console.WriteLine($\"Feature flag changed to: {enabled}\");\n    }\n};\n\n// Works with complex types too\nreplane.ConfigChanged += (sender, e) =\u003e\n{\n    if (e.ConfigName == \"theme\")\n    {\n        var theme = e.GetValue\u003cThemeConfig\u003e();\n        Console.WriteLine($\"Theme updated: dark={theme?.DarkMode}\");\n    }\n};\n\n// Unsubscribe when needed\nvoid OnConfigChanged(object? sender, ConfigChangedEventArgs e)\n{\n    Console.WriteLine($\"Config changed: {e.ConfigName}\");\n}\n\nreplane.ConfigChanged += OnConfigChanged;\n// Later...\nreplane.ConfigChanged -= OnConfigChanged;\n```\n\n### Default Values\n\nProvide default values for when configs aren't loaded:\n\n```csharp\nvar replane = new ReplaneClient(new ReplaneClientOptions\n{\n    Defaults = new Dictionary\u003cstring, object?\u003e\n    {\n        [\"feature-enabled\"] = false,\n        [\"rate-limit\"] = 100\n    }\n});\n```\n\n### Required Configs\n\nEnsure specific configs are present on initialization:\n\n```csharp\nvar replane = new ReplaneClient(new ReplaneClientOptions\n{\n    Required = [\"essential-config\", \"api-endpoint\"]\n});\n\n// ConnectAsync will throw if required configs are missing\nawait replane.ConnectAsync(new ConnectOptions\n{\n    BaseUrl = \"https://your-server.com\",\n    SdkKey = \"your-key\"\n});\n```\n\n## Testing\n\nUse the in-memory client for unit tests:\n\n```csharp\nusing Replane.Testing;\n\n[Fact]\npublic void TestFeatureFlag()\n{\n    // Create test client with initial configs\n    using var client = TestClient.Create(new Dictionary\u003cstring, object?\u003e\n    {\n        [\"feature-enabled\"] = true,\n        [\"max-items\"] = 50\n    });\n\n    // Use like the real client\n    client.Get\u003cbool\u003e(\"feature-enabled\").Should().BeTrue();\n    client.Get\u003cint\u003e(\"max-items\").Should().Be(50);\n}\n```\n\n### Testing with Overrides\n\n```csharp\n[Fact]\npublic void TestOverrides()\n{\n    using var client = TestClient.Create();\n\n    // Set up config with overrides\n    client.SetConfigWithOverrides(\n        name: \"premium-feature\",\n        value: false,\n        overrides: [\n            new OverrideData\n            {\n                Name = \"premium-users\",\n                Conditions = [\n                    new ConditionData\n                    {\n                        Operator = \"equals\",\n                        Property = \"plan\",\n                        Expected = \"premium\"\n                    }\n                ],\n                Value = true\n            }\n        ]);\n\n    // Test with different contexts\n    client.Get\u003cbool\u003e(\"premium-feature\", new ReplaneContext { [\"plan\"] = \"free\" })\n        .Should().BeFalse();\n\n    client.Get\u003cbool\u003e(\"premium-feature\", new ReplaneContext { [\"plan\"] = \"premium\" })\n        .Should().BeTrue();\n}\n```\n\n### Testing Segmentation\n\n```csharp\n[Fact]\npublic void TestABTest()\n{\n    using var client = TestClient.Create();\n\n    client.SetConfigWithOverrides(\n        name: \"ab-test\",\n        value: \"control\",\n        overrides: [\n            new OverrideData\n            {\n                Name = \"treatment-group\",\n                Conditions = [\n                    new ConditionData\n                    {\n                        Operator = \"segmentation\",\n                        Property = \"user_id\",\n                        FromPercentage = 0,\n                        ToPercentage = 50,\n                        Seed = \"experiment-seed\"\n                    }\n                ],\n                Value = \"treatment\"\n            }\n        ]);\n\n    // Result is deterministic for each user\n    var result = client.Get\u003cstring\u003e(\"ab-test\", new ReplaneContext { [\"user_id\"] = \"user-123\" });\n    // Will consistently be either \"control\" or \"treatment\" for this user\n}\n```\n\n### Testing Config Changes\n\n```csharp\n[Fact]\npublic void TestConfigChangeEvent()\n{\n    using var client = TestClient.Create();\n\n    var receivedEvents = new List\u003cConfigChangedEventArgs\u003e();\n    client.ConfigChanged += (sender, e) =\u003e receivedEvents.Add(e);\n\n    client.Set(\"feature\", true);\n    client.Set(\"feature\", false);\n\n    receivedEvents.Should().HaveCount(2);\n    receivedEvents[0].GetValue\u003cbool\u003e().Should().BeTrue();\n    receivedEvents[1].GetValue\u003cbool\u003e().Should().BeFalse();\n}\n```\n\n### Testing Complex Types\n\n```csharp\npublic record FeatureFlags\n{\n    public bool NewUI { get; init; }\n    public List\u003cstring\u003e EnabledModules { get; init; } = [];\n}\n\n[Fact]\npublic void TestComplexType()\n{\n    var flags = new FeatureFlags\n    {\n        NewUI = true,\n        EnabledModules = [\"dashboard\", \"analytics\"]\n    };\n\n    using var client = TestClient.Create(new Dictionary\u003cstring, object?\u003e\n    {\n        [\"features\"] = flags\n    });\n\n    var result = client.Get\u003cFeatureFlags\u003e(\"features\");\n\n    result!.NewUI.Should().BeTrue();\n    result.EnabledModules.Should().Contain(\"dashboard\");\n}\n\n[Fact]\npublic void TestComplexTypeWithOverrides()\n{\n    using var client = TestClient.Create();\n\n    var defaultTheme = new ThemeConfig { DarkMode = false, PrimaryColor = \"#000\", FontSize = 12 };\n    var premiumTheme = new ThemeConfig { DarkMode = true, PrimaryColor = \"#FFD700\", FontSize = 16 };\n\n    client.SetConfigWithOverrides(\n        name: \"theme\",\n        value: defaultTheme,\n        overrides: [\n            new OverrideData\n            {\n                Name = \"premium-theme\",\n                Conditions = [\n                    new ConditionData { Operator = \"equals\", Property = \"plan\", Expected = \"premium\" }\n                ],\n                Value = premiumTheme\n            }\n        ]);\n\n    client.Get\u003cThemeConfig\u003e(\"theme\", new ReplaneContext { [\"plan\"] = \"free\" })!\n        .DarkMode.Should().BeFalse();\n\n    client.Get\u003cThemeConfig\u003e(\"theme\", new ReplaneContext { [\"plan\"] = \"premium\" })!\n        .DarkMode.Should().BeTrue();\n}\n```\n\n## Dependency Injection\n\nBoth `ReplaneClient` and `InMemoryReplaneClient` implement the `IReplaneClient` interface, making it easy to swap implementations for testing or use with dependency injection:\n\n### ASP.NET Core Registration\n\n```csharp\n// Program.cs\nvar builder = WebApplication.CreateBuilder(args);\n\n// Register Replane client as the interface\nbuilder.Services.AddSingleton\u003cIReplaneClient\u003e(sp =\u003e\n{\n    var client = new ReplaneClient(new ReplaneClientOptions\n    {\n        BaseUrl = builder.Configuration[\"Replane:BaseUrl\"]!,\n        SdkKey = builder.Configuration[\"Replane:SdkKey\"]!\n    });\n    return client;\n});\n\nvar app = builder.Build();\n\n// Connect on startup\nvar replane = app.Services.GetRequiredService\u003cIReplaneClient\u003e();\nif (replane is ReplaneClient realClient)\n{\n    await realClient.ConnectAsync();\n}\n\n// Use in controllers/services\napp.MapGet(\"/api/items\", (IReplaneClient replane) =\u003e\n{\n    var maxItems = replane.Get\u003cint\u003e(\"max-items\", defaultValue: 100);\n    return Results.Ok(new { maxItems });\n});\n\napp.Run();\n```\n\n### Using in Services\n\n```csharp\npublic class FeatureService\n{\n    private readonly IReplaneClient _replane;\n\n    public FeatureService(IReplaneClient replane)\n    {\n        _replane = replane;\n    }\n\n    public bool IsFeatureEnabled(string userId)\n    {\n        return _replane.Get\u003cbool\u003e(\"new-feature\", new ReplaneContext\n        {\n            [\"user_id\"] = userId\n        });\n    }\n}\n```\n\n### Testing with DI\n\n```csharp\n[Fact]\npublic void TestFeatureService()\n{\n    // Create test client implementing IReplaneClient\n    using var testClient = TestClient.Create(new Dictionary\u003cstring, object?\u003e\n    {\n        [\"new-feature\"] = true\n    });\n\n    // Inject into service\n    var service = new FeatureService(testClient);\n\n    // Test the service\n    service.IsFeatureEnabled(\"user-123\").Should().BeTrue();\n}\n```\n\n## Configuration Options\n\n### ReplaneClientOptions\n\nOptions passed to the constructor. Connection options are provided via `ConnectAsync`.\n\n| Option       | Type                          | Default | Description                     |\n| ------------ | ----------------------------- | ------- | ------------------------------- |\n| `Context`    | `ReplaneContext`              | `null`  | Default context for evaluations |\n| `Defaults`   | `Dictionary\u003cstring, object?\u003e` | `null`  | Default values                  |\n| `Required`   | `IReadOnlyList\u003cstring\u003e`       | `null`  | Required config names           |\n| `HttpClient` | `HttpClient`                  | `null`  | Custom HttpClient               |\n| `Debug`      | `bool`                        | `false` | Enable debug logging            |\n| `Logger`     | `IReplaneLogger`              | `null`  | Custom logger implementation    |\n\n### ConnectOptions\n\nConnection options passed to `ConnectAsync`.\n\n| Option               | Type     | Default  | Description                |\n| -------------------- | -------- | -------- | -------------------------- |\n| `BaseUrl`            | `string` | required | Replane server URL         |\n| `SdkKey`             | `string` | required | SDK key for authentication |\n| `RequestTimeoutMs`   | `int`    | `2000`   | HTTP request timeout       |\n| `ConnectionTimeoutMs`| `int`    | `5000`   | Initial connection timeout |\n| `RetryDelayMs`       | `int`    | `200`    | Initial retry delay        |\n| `InactivityTimeoutMs`| `int`    | `30000`  | SSE inactivity timeout     |\n| `Agent`              | `string` | `null`   | Agent identifier           |\n\n## Debug Logging\n\nEnable debug logging to troubleshoot issues:\n\n```csharp\nvar replane = new ReplaneClient(new ReplaneClientOptions\n{\n    Debug = true\n});\n\nawait replane.ConnectAsync(new ConnectOptions\n{\n    BaseUrl = \"https://your-server.com\",\n    SdkKey = \"your-key\"\n});\n```\n\nThis outputs detailed logs including:\n\n- Client initialization with all options\n- SSE connection lifecycle (connect, reconnect, disconnect)\n- Every `Get()` call with config name, context, and result\n- Override evaluation details (which conditions matched/failed)\n- Raw SSE event data\n\nExample output:\n\n```\n[DEBUG] Replane: Initializing ReplaneClient with options:\n[DEBUG] Replane:   BaseUrl: https://your-server.com\n[DEBUG] Replane:   SdkKey: your...key\n[DEBUG] Replane: Connecting to SSE: https://your-server.com/api/sdk/v1/replication/stream\n[DEBUG] Replane: SSE event received: type=init\n[DEBUG] Replane: Initialization complete: 5 configs loaded\n[DEBUG] Replane: Get\u003cBoolean\u003e(\"feature-flag\") called\n[DEBUG] Replane:   Config \"feature-flag\" found, base value: false, overrides: 1\n[DEBUG] Replane:     Evaluating override #0 (conditions: property(plan equals \"premium\"))\n[DEBUG] Replane:       Condition: property \"plan\" (\"premium\") equals \"premium\" =\u003e Matched\n[DEBUG] Replane:   Override #0 matched, returning: true\n```\n\n### Custom Logger\n\nProvide your own logger implementation:\n\n```csharp\npublic class MyLogger : IReplaneLogger\n{\n    public void Log(LogLevel level, string message, Exception? exception = null)\n    {\n        // Forward to your logging framework\n        _logger.Log(MapLevel(level), exception, message);\n    }\n}\n\nvar replane = new ReplaneClient(new ReplaneClientOptions\n{\n    BaseUrl = \"https://your-server.com\",\n    SdkKey = \"your-key\",\n    Logger = new MyLogger()\n});\n```\n\n## Condition Operators\n\nThe SDK supports the following condition operators for overrides:\n\n| Operator                | Description                |\n| ----------------------- | -------------------------- |\n| `equals`                | Exact match                |\n| `in`                    | Value is in list           |\n| `not_in`                | Value is not in list       |\n| `less_than`             | Less than comparison       |\n| `less_than_or_equal`    | Less than or equal         |\n| `greater_than`          | Greater than comparison    |\n| `greater_than_or_equal` | Greater than or equal      |\n| `segmentation`          | Percentage-based bucketing |\n| `and`                   | All conditions must match  |\n| `or`                    | Any condition must match   |\n| `not`                   | Negate a condition         |\n\n## Error Handling\n\n```csharp\ntry\n{\n    await replane.ConnectAsync();\n    var value = replane.Get\u003cstring\u003e(\"my-config\");\n}\ncatch (AuthenticationException)\n{\n    // Invalid SDK key\n}\ncatch (ConfigNotFoundException ex)\n{\n    // Config doesn't exist\n    Console.WriteLine($\"Config not found: {ex.ConfigName}\");\n}\ncatch (ReplaneTimeoutException ex)\n{\n    // Operation timed out\n    Console.WriteLine($\"Timeout after {ex.TimeoutMs}ms\");\n}\ncatch (ReplaneException ex)\n{\n    // Other errors\n    Console.WriteLine($\"Error [{ex.Code}]: {ex.Message}\");\n}\n```\n\n## Examples\n\nSee the [examples](./examples/) directory for complete working examples:\n\n| Example                                                  | Description                                        |\n| -------------------------------------------------------- | -------------------------------------------------- |\n| [BasicUsage](./examples/BasicUsage/)                     | Simple console app with basic config reading       |\n| [ConsoleWithOverrides](./examples/ConsoleWithOverrides/) | Context-based overrides and user segmentation      |\n| [BackgroundWorker](./examples/BackgroundWorker/)         | Long-running service with real-time config updates |\n| [WebApiIntegration](./examples/WebApiIntegration/)       | ASP.NET Core Web API with middleware and DI        |\n| [UnitTesting](./examples/UnitTesting/)                   | Unit testing with the in-memory test client        |\n\nEach example is self-contained and can be copied and run independently.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and contribution guidelines.\n\n## Community\n\nHave questions or want to discuss Replane? Join the conversation in [GitHub Discussions](https://github.com/orgs/replane-dev/discussions).\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freplane-dev%2Freplane-dotnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freplane-dev%2Freplane-dotnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freplane-dev%2Freplane-dotnet/lists"}