{"id":18009884,"url":"https://github.com/kekyo/fluorite","last_synced_at":"2025-10-08T13:46:51.277Z","repository":{"id":84090117,"uuid":"348162060","full_name":"kekyo/Fluorite","owner":"kekyo","description":"Simplest and fully-customizable RPC standalone infrastructure.","archived":false,"fork":false,"pushed_at":"2021-09-14T12:38:20.000Z","size":522,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-04T21:33:59.433Z","etag":null,"topics":["async","bidirectional","complementary","dotnet","full-duplex","json","rpc-client","rpc-server","standalone","transport","websocket"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kekyo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2021-03-16T00:24:47.000Z","updated_at":"2024-04-24T15:58:01.000Z","dependencies_parsed_at":"2023-06-09T06:45:17.524Z","dependency_job_id":null,"html_url":"https://github.com/kekyo/Fluorite","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/kekyo/Fluorite","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kekyo%2FFluorite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kekyo%2FFluorite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kekyo%2FFluorite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kekyo%2FFluorite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kekyo","download_url":"https://codeload.github.com/kekyo/Fluorite/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kekyo%2FFluorite/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278956298,"owners_count":26075221,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"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":["async","bidirectional","complementary","dotnet","full-duplex","json","rpc-client","rpc-server","standalone","transport","websocket"],"created_at":"2024-10-30T02:11:25.856Z","updated_at":"2025-10-08T13:46:51.259Z","avatar_url":"https://github.com/kekyo.png","language":"C#","readme":"# Fluorite\n\n![Fluorite](Images/Fluorite.160.png)\n\nFluorite - Simplest and fully-customizable RPC standalone infrastructure on .NET\n\n[![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)\n\n## NuGet\n\n|Package|main|devel|Description|\n|:--|:--|:--|:--|\n|Fluorite|[![NuGet Fluorite](https://img.shields.io/nuget/v/Fluorite.svg?style=flat)](https://www.nuget.org/packages/Fluorite)|[![MyGet Fluorite](https://img.shields.io/myget/fluorite/v/Fluorite.svg?style=flat\u0026label=myget)](https://www.myget.org/feed/fluorite/package/nuget/Fluorite)|Meta-package (Provides automated generating static proxy / **Experimental**)|\n|Fluorite.Dynamic|[![NuGet Fluorite.Dynamic](https://img.shields.io/nuget/v/Fluorite.Dynamic.svg?style=flat)](https://www.nuget.org/packages/Fluorite.Dynamic)|[![MyGet Fluorite.Dynamic](https://img.shields.io/myget/fluorite/v/Fluorite.Dynamic.svg?style=flat\u0026label=myget)](https://www.myget.org/feed/fluorite/package/nuget/Fluorite.Dynamic)|Meta-package (Provides automated generating dynamic proxy)|\n\n### Sub packages\n\n|Package|main|devel|Description|\n|:--|:--|:--|:--|\n|Fluorite.Core|[![NuGet Fluorite.Core](https://img.shields.io/nuget/v/Fluorite.Core.svg?style=flat)](https://www.nuget.org/packages/Fluorite.Core)|[![MyGet Fluorite.Core](https://img.shields.io/myget/fluorite/v/Fluorite.Core.svg?style=flat\u0026label=myget)](https://www.myget.org/feed/fluorite/package/nuget/Fluorite.Core)|Independed core engine|\n|Fluorite.Serializer|[![NuGet Fluorite.Serializer](https://img.shields.io/nuget/v/Fluorite.Serializer.svg?style=flat)](https://www.nuget.org/packages/Fluorite.Serializer)|[![MyGet Fluorite.Serializer](https://img.shields.io/myget/fluorite/v/Fluorite.Serializer.svg?style=flat\u0026label=myget)](https://www.myget.org/feed/fluorite/package/nuget/Fluorite.Serializer)|Default serializer implementation (Newtonsoft.Json)|\n|Fluorite.Transport|[![NuGet Fluorite.Transport](https://img.shields.io/nuget/v/Fluorite.Transport.svg?style=flat)](https://www.nuget.org/packages/Fluorite.Transport)|[![MyGet Fluorite.Transport](https://img.shields.io/myget/fluorite/v/Fluorite.Transport.svg?style=flat\u0026label=myget)](https://www.myget.org/feed/fluorite/package/nuget/Fluorite.Transport)|Default transport implementation (System.Net.WebSockets)|\n|Fluorite.Build|[![NuGet Fluorite.Build](https://img.shields.io/nuget/v/Fluorite.Build.svg?style=flat)](https://www.nuget.org/packages/Fluorite.Build)|[![MyGet Fluorite.Build](https://img.shields.io/myget/fluorite/v/Fluorite.Build.svg?style=flat\u0026label=myget)](https://www.myget.org/feed/fluorite/package/nuget/Fluorite.Build)|Automated static proxy generator at building time|\n\n## CI\n\n|main|devel|\n|:--|:--|\n|[![Fluorite CI build (main)](https://github.com/kekyo/Fluorite/workflows/.NET/badge.svg?branch=main)](https://github.com/kekyo/Fluorite/actions?query=branch%3Amain)|[![Fluorite CI build (devel)](https://github.com/kekyo/Fluorite/workflows/.NET/badge.svg?branch=devel)](https://github.com/kekyo/Fluorite/actions?query=branch%3Adevel)|\n\n-----\n\n## What is this ?\n\nAn implementation of bi-directional/complementary/asynchronous RPC (remote procedure call) controller with customizable serializer/transport interface.\n\nThis is simple diagram for user side Fluorite architecture:\n\n![Fluorite Diagram](./Images/diagram.png)\n\nFluorite doesn't have any depending other large libraries (For example: ASP.NET).\nYou can easy integrate flexible RPC messaging system on your own application.\n\n### Features\n\n* Can do truly bi-directional operation between server and client.\n  * Complementary interface.\n  * With overlapping calls and will do non-blocking (if transport has capability).\n* Fully asynchronous sending/receiving operation with `ValueTask\u003cT\u003e` type.\n* RPC form on custom interface definition.\n  * You can choose static proxy (at building time) or dynamic proxy (at runtime).\n  * Or you can attach your own custom proxy generator.\n* Can attach your own custom serializer likes Json or another form by simple interface.\n  * Json, Bson, XML, Message Pack, Protocol Buffer and etc...\n  * Will make simpler logical message format.\n* Can attach your own custom transport likes WebSocket or another protocol by simple interface.\n  * WebSocket, TCP/UDP direct, Pipe, IPC, MQ, Persistence data and etc...\n\n-----\n\n## Getting started\n\nDefault configuration (meta-package) is applied with:\n\n* Will use serializer `Newtonsoft.Json`.\n* Will use transport `System.Net.WebSockets`.\n  * Client side: `ClientWebSocket`.\n  * Server side: `WebSocket` on `HttpListener`.\n\nNOTE: When running on Windows platform, you have to configure `HttpListener`/`HTTP.SYS` related setup protocol on server side. For example:\n\n1. `netsh http add urlacl url=http://+:4649/ user=everyone`\n2. `netsh advfirewall firewall add rule name=\"Fluorite HTTP\" dir=in action=allow`\n3. `netsh advfirewall firewall set rule name=\"Fluorite HTTP\" new program=system profile=private protocol=tcp localport=4649`\n\n### Custom RPC interface definition\n\nWe can use independent platform such as `netstandard2.0` or likes.\n\n```csharp\n// nuget install Fluorite\n// (or Fluorite.Build using static proxy generator)\nusing Fluorite;\n\n// You can use custom types each arguments/return value, constraint depending used serializer.\n// (Default serializer is Newtonsoft.Json)\npublic sealed class Item\n{\n    public int Id;\n    public string Label;\n    public int Price;\n}\n\n// Require inherits `IHost` and method returns ValueTask\u003cT\u003e or ValueTask.\npublic interface IPartsShop : IHost\n{\n    ValueTask\u003cItem[]\u003e GetItemsAsync(string category, int max);\n    ValueTask PurchaseAsync(params int[] itemIds);\n}\n```\n\n### Client side\n\n```csharp\n// nuget install Fluorite\nusing Fluorite;\n\n// Initialize\nNest.Factory.Initialize();\n\n// Connect to server with default WebSocket/Json transport.\n// (Optional: You can register client side expose object at last arguments same as server side)\nvar nest = await Nest.Factory.ConnectAsync(\"server.example.com\", 4649, false);\ntry\n{\n    // Get transparent proxy instance from the client nest.\n    var partsShop = nest.GetPeer\u003cIPartsShop\u003e();\n\n    // Manipulate via transparent proxy.\n    var items = await shop.GetItemsAsync(\"FPGA\", 100);\n    await shop.PurchaseAsync(items[3].Id);\n}\nfinally\n{\n    // Shutdown client nest\n    await nest.ShutdownAsync();\n}\n```\n\n### Server side\n\n```csharp\n// nuget install Fluorite\nusing Fluorite;\n\n// The exposed interface implementer.\ninternal sealed class PartsShop : IPartsShop\n{\n    public async ValueTask\u003cItem[]\u003e GetItemsAsync(string category, int max)\n    {\n        // ...\n    }\n\n    public async ValueTask PurchaseAsync(int[] itemIds)\n    {\n        // ...\n    }\n}\n\n// Initialize\nNest.Factory.Initialize();\n\n// Start default WebSocket/Json server (at background)\n// with expose objects at last arguments.\nvar nest = await Nest.Factory.StartServer(4649, false, new PartsShop());\ntry\n{\n    // ...\n}\nfinally\n{\n    // Shutdown server nest\n    await nest.ShutdownAsync();\n}\n```\n\n-----\n\n## Transparent proxy\n\nFluorite requires transparent proxy implementation. You can choice few implementation ways:\n\n* Automatic static proxy generator:\n  * `Fluorite.Build` (or `Fluorite`) package will generate automatically static transparent proxy class on your project.\n  * It's easy way and recommended.\n  * AOT environment friendly and fastest at runtime.\n  * But some .NET development environemnt couldn't use. Unity (Game engine) and others have unique way to build environment.\n* Dynamic proxy generator:\n  * `Fluorite.Dynamic` package will generate automatically dynamic transparent proxy class at on demand (runtime).\n  * Stable mostly environments.\n  * It's better performance, will use CIL/MSIL generator.\n  * Maybe couldn't use on AOT platform.\n* Self implements static proxy:\n  * Flexible and customizable.\n  * You dodge referring meta-package `Fluorite` or `Fluorite.Dynamic`. Will make decrease dependency only referring `Fluorite.Core`.\n  * Tired :)\n\n## Automatic static proxy generator (Fluorite.Build)\n\nYou don't need anything. It's an example result:\n\n```csharp\n// It's automatically generated, you can see it by ILSpy and others.\ninternal sealed class IPartsShopProxy__ :\n    GeneratedProxyBase, IPartsShop\n{\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public ValueTask\u003cItem[]\u003e GetItemsAsync(string category, int max)\n    {\n        return base.InvokeAsync\u003cItem[]\u003e(\"ExamplePartsShop.IPartsShop.GetItems\", new object[2] { category, max });\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public ValueTask PurchaseAsync(int[] itemIds)\n    {\n        return base.InvokeAsync(\"ExamplePartsShop.IPartsShop.Purchase\", new object[1] { itemIds });\n    }\n}\n```\n\n## Automatic dynamic proxy generator (use Fluorite.Dynamic)\n\n\n\n## Self implements static proxy\n\nYou have to implement likes:\n\n```csharp\n// Hand coded static proxy class.\ninternal sealed class PartsShopProxy :\n    StaticProxyBase, IPartsShop\n{\n    public ValueTask\u003cItem[]\u003e GetItemsAsync(string category, int max)\n    {\n        return base.InvokeAsync\u003cIPartsShop, Item[]\u003e(\"GetItems\", category, max);\n    }\n\n    public ValueTask PurchaseAsync(int[] itemIds)\n    {\n        // HACK: Requires cast expression because will convert implicitly param array.\n        return base.InvokeAsync\u003cIPartsShop\u003e(\"Purchase\", (object)itemIds);\n    }\n}\n\n// Register proxy class.\nNest.Register\u003cIPartsShop, PartsShopProxy\u003e();\n```\n\n\n-----\n\n## Advanced topic\n\n### Customize with your own serializer\n\nTODO:\n\n### Customize with your own transport\n\nTODO:\n\n### Customize with your own proxy factory\n\nTODO:\n\n-----\n\n## License\n\nApache-v2\n\n## History\n\n* 0.6.0:\n  * Reconstruct transport architecture overall transferring data with `Stream` asynchronously.\n  * Reconstruct transport architecture enabling truly full-duplex on WebSocket.\n* 0.5.0:\n  * Implemented static proxy generator at running build time (Fluorite.Build)\n* 0.2.0:\n  * Passed full-duplex, bi-directional tests.\n* 0.1.0:\n  * Initial version.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkekyo%2Ffluorite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkekyo%2Ffluorite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkekyo%2Ffluorite/lists"}