![]() |
VOOZH | about |
dotnet add package CrossCloudKit.File.AWS --version 2026.4.22.71
NuGet\Install-Package CrossCloudKit.File.AWS -Version 2026.4.22.71
<PackageReference Include="CrossCloudKit.File.AWS" Version="2026.4.22.71" />
<PackageVersion Include="CrossCloudKit.File.AWS" Version="2026.4.22.71" />Directory.Packages.props
<PackageReference Include="CrossCloudKit.File.AWS" />Project file
paket add CrossCloudKit.File.AWS --version 2026.4.22.71
#r "nuget: CrossCloudKit.File.AWS, 2026.4.22.71"
#:package CrossCloudKit.File.AWS@2026.4.22.71
#addin nuget:?package=CrossCloudKit.File.AWS&version=2026.4.22.71Install as a Cake Addin
#tool nuget:?package=CrossCloudKit.File.AWS&version=2026.4.22.71Install as a Cake Tool
π License: MIT
π .NET 8
π Tests
CrossCloudKit is a comprehensive .NET library that provides unified interfaces and implementations for working with multiple cloud services. It enables developers to write cloud-agnostic code that can seamlessly work across AWS, Google Cloud, MongoDB, Redis, and S3-compatible storage providers with consistent APIs and behavior.
Last Updated: 2026-04-22 13:09:39 UTC
| Metric | Count |
|---|---|
| β Tests Passed | 2284 |
| β Tests Failed | 0 |
| π Total Tests | 2284 |
EmbedAndUpsertAsync, EmbedAndUpsertBatchAsync, SemanticSearchAsync) for common embed-then-store and semantic-search workflows β single-line, no boilerplatePrimitive systemIDistributedCache adapter (MemoryServiceDistributedCache) and IFileProvider (FileServiceFileProvider) bridge for seamless framework integration| Package | Description |
|---|---|
CrossCloudKit.Interfaces |
Core interfaces, base classes, and ASP.NET Core integration |
| Database Services | |
CrossCloudKit.Database.AWS |
AWS DynamoDB implementation |
CrossCloudKit.Database.Mongo |
MongoDB implementation |
CrossCloudKit.Database.GC |
Google Cloud Datastore implementation |
CrossCloudKit.Database.Basic |
Cross-process file-based database implementation |
| File Storage Services | |
CrossCloudKit.File.AWS |
AWS S3 file storage implementation |
CrossCloudKit.File.GC |
Google Cloud Storage implementation |
CrossCloudKit.File.S3Compatible |
S3-compatible storage providers |
CrossCloudKit.File.Basic |
Cross-process file-based storage implementation |
| PubSub Messaging Services | |
CrossCloudKit.PubSub.AWS |
AWS SNS/SQS Hybrid implementation |
CrossCloudKit.PubSub.GC |
Google Cloud Pub/Sub implementation |
CrossCloudKit.PubSub.Redis |
Redis Pub/Sub implementation |
CrossCloudKit.PubSub.Basic |
Cross-process file-based Pub/Sub implementation |
| Memory/Caching Services | |
CrossCloudKit.Memory.Redis |
Redis memory and caching implementation |
CrossCloudKit.Memory.Basic |
Cross-process file-based memory implementation |
| LLM Services | |
CrossCloudKit.LLM.OpenAI |
OpenAI-compatible LLM provider β works with OpenAI, Azure OpenAI, Ollama, Groq, Bedrock, LM Studio, and any OpenAI-compatible endpoint |
CrossCloudKit.LLM.Basic |
β‘ CPU-only plug & play meta-package β installs both sub-packages below; completions + embeddings work out of the box |
CrossCloudKit.LLM.Basic.Embeddings |
Embeddings only β bundles snowflake-arctic-embed-m-long (Q8_0) via LLamaSharp CPU backend; works with zero configuration |
CrossCloudKit.LLM.Basic.Completion |
Completions only β bundles SmolLM2-135M-Instruct (Q8_0, ~139 MB, Apache-2.0) + LLamaSharp CPU backend; works with zero configuration |
| Vector Database Services | |
CrossCloudKit.Vector.Basic |
Cross-process file-based vector store β zero external dependencies, ideal for development, testing, and lightweight workloads |
CrossCloudKit.Vector.Qdrant |
Qdrant vector database via official gRPC client |
| Debug Panel | |
CrossCloudKit.Basic.DebugPanel |
Embedded HTTP debug dashboard for all Basic service providers β live service cards, operation log with SSE, interactive data browsing, automatic dead-process cleanup |
| Utilities | |
CrossCloudKit.Utilities.Common |
Common utilities and primitive types |
CrossCloudKit.Utilities.Windows |
Windows-specific utilities |
# Choose your cloud provider package(s)
# Database Services
dotnet add package CrossCloudKit.Database.AWS
dotnet add package CrossCloudKit.Database.Mongo
dotnet add package CrossCloudKit.Database.GC
dotnet add package CrossCloudKit.Database.Basic
# File Storage Services
dotnet add package CrossCloudKit.File.AWS
dotnet add package CrossCloudKit.File.GC
dotnet add package CrossCloudKit.File.S3Compatible
dotnet add package CrossCloudKit.File.Basic
# PubSub Services
dotnet add package CrossCloudKit.PubSub.AWS
dotnet add package CrossCloudKit.PubSub.GC
dotnet add package CrossCloudKit.PubSub.Redis
dotnet add package CrossCloudKit.PubSub.Basic
# Memory Services
dotnet add package CrossCloudKit.Memory.Redis
dotnet add package CrossCloudKit.Memory.Basic
# LLM Services
dotnet add package CrossCloudKit.LLM.OpenAI # For any OpenAI-compatible endpoint
dotnet add package CrossCloudKit.LLM.Basic # CPU-only meta-package (embeddings + completions bundled)
dotnet add package CrossCloudKit.LLM.Basic.Embeddings # Embeddings only (bundles snowflake-arctic-embed-m-long GGUF)
dotnet add package CrossCloudKit.LLM.Basic.Completion # Completions only (bundles SmolLM2-135M GGUF)
# Vector Database Services
dotnet add package CrossCloudKit.Vector.Basic # File-based, no infra needed
dotnet add package CrossCloudKit.Vector.Qdrant # Qdrant gRPC client
# Core interfaces (automatically included as dependency)
dotnet add package CrossCloudKit.Interfaces
using CrossCloudKit.LLM.OpenAI;
using CrossCloudKit.Interfaces.Records;
using CrossCloudKit.Interfaces.Enums;
// Works with OpenAI, Ollama, Groq, Azure OpenAI, Bedrock, LM Studio, etc.
// embeddingModel is optional β use it when your completion and embedding models differ
// (the common case on self-hosted endpoints like Ollama).
await using var llmService = new LLMServiceOpenAI(
baseUrl: "http://localhost:11434/v1", // Ollama
apiKey: "",
defaultModel: "qwen2.5:14b", // used for completions
embeddingModel: "nomic-embed-text:v1.5" // used for CreateEmbeddingAsync / CreateEmbeddingsAsync
);
// If embeddingModel is omitted it falls back to defaultModel.
// Text completion
var response = await llmService.CompleteAsync(new LLMRequest
{
Messages =
[
new LLMMessage { Role = LLMRole.System, Content = "You are a helpful assistant." },
new LLMMessage { Role = LLMRole.User, Content = "Explain quantum computing briefly." }
],
MaxTokens = 256
});
Console.WriteLine(response.Data.Content);
// Streaming completion
await foreach (var chunk in llmService.CompleteStreamingAsync(request))
{
if (!chunk.Data.IsFinal)
Console.Write(chunk.Data.ContentDelta);
}
// Embeddings
var embedding = await llmService.CreateEmbeddingAsync("Hello, world!");
Console.WriteLine($"Dimensions: {embedding.Data.Length}");
using CrossCloudKit.LLM.Basic;
using CrossCloudKit.Interfaces.Records;
using CrossCloudKit.Interfaces.Enums;
// Everything works out of the box β no downloads, no API keys, no configuration.
// β’ Embeddings: snowflake-arctic-embed-m-long (Q8_0) bundled via LLamaSharp
// β’ Completions: SmolLM2-135M-Instruct (Q8_0, ~139 MB) bundled in the NuGet package
// To use a different GGUF model, pass its path or set LLM_BASIC_MODEL_PATH / LLM_BASIC_EMBEDDING_MODEL_PATH.
await using var llmService = new LLMServiceBasic();
// Embeddings β always available
var vec = await llmService.CreateEmbeddingAsync("semantic search text");
// vec.Data is float[] (768-dim for snowflake-arctic-embed-m-long)
// Batch embeddings
var vecs = await llmService.CreateEmbeddingsAsync(["text a", "text b"]);
// Completions β available out of the box with the bundled SmolLM2-135M model
var reply = await llmService.CompleteAsync(new LLMRequest
{
Messages = [new LLMMessage { Role = LLMRole.User, Content = "Say hello." }],
MaxTokens = 64
});
Console.WriteLine(reply.Data.Content);
// Streaming completions
await foreach (var chunk in llmService.CompleteStreamingAsync(new LLMRequest
{
Messages = [new LLMMessage { Role = LLMRole.User, Content = "Explain gravity briefly." }],
MaxTokens = 128
}))
{
if (!chunk.Data.IsFinal)
Console.Write(chunk.Data.ContentDelta);
}
using CrossCloudKit.LLM.Basic.Embeddings;
// Bundles snowflake-arctic-embed-m-long (Q8_0); no completion model overhead.
await using var embedder = new LLMEmbeddingServiceBasic();
var vec = await embedder.CreateEmbeddingAsync("lightweight embedding");
using CrossCloudKit.LLM.Basic.Completion;
// Bundles SmolLM2-135M-Instruct; no embedding model overhead.
await using var completer = new LLMCompletionServiceBasic();
var reply = await completer.CompleteAsync(new LLMRequest
{
Messages = [new LLMMessage { Role = LLMRole.User, Content = "Hello!" }],
MaxTokens = 64
});
Console.WriteLine(reply.Data.Content);
using CrossCloudKit.Vector.Basic;
using CrossCloudKit.Interfaces.Enums;
using CrossCloudKit.Interfaces.Records;
using CrossCloudKit.Utilities.Common;
using Newtonsoft.Json.Linq;
await using var vectorService = new VectorServiceBasic();
// Create collection
await vectorService.EnsureCollectionExistsAsync("products", vectorDimensions: 384, VectorDistanceMetric.Cosine);
// Upsert points
await vectorService.UpsertAsync("products", new VectorPoint
{
Id = Guid.NewGuid().ToString(),
Vector = new float[384], // typically from llmService.CreateEmbeddingAsync
Metadata = new JObject { ["name"] = "Widget", ["price"] = 9.99, ["inStock"] = true }
});
// Similarity search with filter
var filter = vectorService.FieldEquals("inStock", new Primitive(true))
.And(vectorService.FieldLessThan("price", new Primitive(20.0)));
var results = await vectorService.QueryAsync("products", queryVector, topK: 5, filter: filter);
foreach (var r in results.Data)
Console.WriteLine($"{r.Id} score={r.Score:F4}");
using CrossCloudKit.Vector.Qdrant;
// Uses the official Qdrant gRPC client (port 6334)
await using var vectorService = new VectorServiceQdrant(
host: "localhost", // or qdrant.common-db.svc.cluster.local
grpcPort: 6334
);
// Same unified IVectorService API as above
await vectorService.EnsureCollectionExistsAsync("embeddings", 384, VectorDistanceMetric.Cosine);
await vectorService.UpsertAsync("embeddings", point);
var hits = await vectorService.QueryAsync("embeddings", queryVec, topK: 10);
using CrossCloudKit.Interfaces; // LLMVectorExtensions lives here
using CrossCloudKit.Interfaces.Records;
using Newtonsoft.Json.Linq;
// ββ Single-item embed-then-upsert ββββββββββββββββββββββββββββββββββββββββββ
var result = await vectorService.EmbedAndUpsertAsync(
llmService: llmService,
collectionName: "docs",
id: "article-42",
text: "cats are mammals",
metadata: new JObject { ["source"] = "wiki" }
);
// ββ Batch embed-then-upsert ββββββββββββββββββββββββββββββββββββββββββββββββ
var items = new List<(string Id, string Text, JObject? Metadata)>
{
("doc-0", "cats are mammals", new JObject { ["text"] = "cats are mammals" }),
("doc-1", "dogs are loyal pets", new JObject { ["text"] = "dogs are loyal pets" }),
("doc-2", "C# is a statically typed language", new JObject { ["text"] = "C# is a statically typed language" })
};
await vectorService.EmbedAndUpsertBatchAsync(llmService, "docs", items);
// ββ Semantic search ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
var hits = await vectorService.SemanticSearchAsync(
llmService: llmService,
collectionName: "docs",
queryText: "what animals are domestic pets?",
topK: 2
);
foreach (var hit in hits.Data)
Console.WriteLine($"[{hit.Score:F3}] {hit.Metadata?["text"]}");
// Embed documents
var texts = new[] { "cats are mammals", "dogs are loyal pets", "C# is a statically typed language" };
var embeddings = await llmService.CreateEmbeddingsAsync(texts);
// Store in vector DB
var points = texts.Zip(embeddings.Data, (t, e) => new VectorPoint
{
Id = Guid.NewGuid().ToString(),
Vector = e,
Metadata = new JObject { ["text"] = t }
}).ToList();
await vectorService.UpsertBatchAsync("docs", points);
// Retrieve semantically similar documents
var queryVec = (await llmService.CreateEmbeddingAsync("what animals are domestic pets?")).Data;
var queryHits = await vectorService.QueryAsync("docs", queryVec, topK: 2, includeMetadata: true);
foreach (var hit in queryHits.Data)
Console.WriteLine($"[{hit.Score:F3}] {hit.Metadata?["text"]}");
using CrossCloudKit.Database.AWS;
// Initialize service
var dbService = new DatabaseServiceAWS(
// Parameters here
);
// Create and store an item
var item = new JObject
{
["Name"] = "John Doe",
["Email"] = "john@example.com",
["Age"] = 30
};
var key = new DbKey("id", new Primitive("user-123"));
await dbService.PutItemAsync("Users", new DbKey("id", key, item);
// Retrieve the item
var result = await dbService.GetItemAsync("Users", key);
if (result.IsSuccessful && result.Data != null)
{
Console.WriteLine($"User: {result.Data["Name"]}");
}
using CrossCloudKit.Database.Mongo;
// Initialize with connection string
var dbService = new DatabaseServiceMongoDB(
// Parameters here
);
// Same unified API
var key = new DbKey("id", new Primitive("user-123"));
await dbService.PutItemAsync("Users", key, item);
using CrossCloudKit.Database.GC;
// Initialize with service account
var dbService = new DatabaseServiceGC(
// Parameters here
);
// Same unified API
var key = new DbKey("id", new Primitive("user-123"));
await dbService.PutItemAsync("Users", key, item);
using CrossCloudKit.File.AWS;
using CrossCloudKit.Interfaces;
// Initialize service
var fileService = new FileServiceAWS(
// Parameters here
);
// Upload a file
var content = StringOrStream.FromString("Hello, World!");
var uploadResult = await fileService.UploadFileAsync(
content: content,
bucketName: "my-bucket",
keyInBucket: "files/hello.txt",
accessibility: FileAccessibility.PublicRead
);
// Download a file
using var memoryStream = new MemoryStream();
var downloadResult = await fileService.DownloadFileAsync(
bucketName: "my-bucket",
keyInBucket: "files/hello.txt",
destination: StringOrStream.FromStream(memoryStream)
);
// Create signed URLs
var signedUploadUrl = await fileService.CreateSignedUploadUrlAsync(
bucketName: "my-bucket",
keyInBucket: "uploads/new-file.txt",
options: new SignedUploadUrlOptions
{
ValidFor = TimeSpan.FromHours(1),
ContentType = "text/plain"
}
);
using CrossCloudKit.File.GC;
// Initialize service
var fileService = new FileServiceGC(
// Parameters here
);
// Same unified API as AWS S3
await fileService.UploadFileAsync(content, "my-bucket", "files/hello.txt");
using CrossCloudKit.File.S3Compatible;
// Initialize for MinIO or other S3-compatible storage
var fileService = new FileServiceS3Compatible(
// Parameters here
);
// Same API as AWS S3
await fileService.UploadFileAsync(content, "my-bucket", "files/hello.txt");
using CrossCloudKit.Memory.Redis;
using CrossCloudKit.Interfaces;
using CrossCloudKit.Utilities.Common;
// Initialize service
var memoryService = new MemoryServiceRedis(
// Parameters here
);
// Create a memory scope
var scope = new MemoryScopeLambda(() => "user:123");
// Set key-value pairs
await memoryService.SetKeyValuesAsync(scope, new[]
{
new KeyValuePair<string, Primitive>("name", new Primitive("John Doe")),
new KeyValuePair<string, Primitive>("age", new Primitive(30L))
});
// Get values
var name = await memoryService.GetKeyValueAsync(scope, "name");
Console.WriteLine($"Name: {name.Data?.AsString}");
// Atomic increment
var newAge = await memoryService.IncrementKeyByValueAndGetAsync(scope, "age", 1);
Console.WriteLine($"New age: {newAge.Data}");
// Distributed mutex locking
using var mutex = await MemoryScopeMutex.CreateScopeAsync(
memoryService, scope, "user-lock", TimeSpan.FromMinutes(5)
);
await mutex.LockAsync();
// Critical section - only one process can execute this at a time
using CrossCloudKit.PubSub.AWS;
using CrossCloudKit.Interfaces;
// Initialize service
var pubSubService = new PubSubServiceAWS(
// Parameters here
);
// Ensure topic exists
await pubSubService.EnsureTopicExistsAsync("user-events");
// Subscribe to messages
await pubSubService.SubscribeAsync(
topic: "user-events",
onMessage: async (topic, message) =>
{
Console.WriteLine($"Received from {topic}: {message}");
},
onError: error =>
{
Console.WriteLine($"Error: {error.Message}");
}
);
// Publish a message
await pubSubService.PublishAsync("user-events", "User logged in");
using CrossCloudKit.PubSub.GC;
// Initialize service
var pubSubService = new PubSubServiceGC(
// Parameters here
);
// Same unified API as AWS
await pubSubService.EnsureTopicExistsAsync("user-events");
await pubSubService.PublishAsync("user-events", "User logged in");
using CrossCloudKit.PubSub.Redis;
// Initialize service
var pubSubService = new PubSubServiceRedis(
// Parameters here
);
// Same API for Redis pub/sub
await pubSubService.SubscribeAsync("user-events", async (topic, message) =>
{
Console.WriteLine($"Redis message from {topic}: {message}");
});
CrossCloudKit provides a powerful, composable conditioning system that works consistently across all database providers. The system supports nested object structures, complex logical operations, and backwards compatibility with simple attribute names.
// Conditional update
var updateData = new JObject { ["LastLogin"] = DateTime.UtcNow };
var key = new DbKey("id", new Primitive("user-123"));
var result = await dbService.UpdateItemAsync(
"Users", key, updateData,
conditions:
dbService.AttributeEquals("IsAdmin", new Primitive(true))
.Or(dbService.AttributeEquals("Status", new Primitive("active"))
.And(dbService.AttributeEquals("IsAdmin", new Primitive(false))))
);
// Check existence with conditions
var exists = await dbService.ItemExistsAsync("Users", "Id", keyValue, condition);
// Check if attribute exists
var condition = dbService.AttributeExists("Email");
var exists = await dbService.ItemExistsAsync(tableName, key, condition);
// Check if attribute does not exist
var notExistsCondition = dbService.AttributeNotExists("MiddleName");
// Equality checks
var equalsCondition = dbService.AttributeEquals("Status", new Primitive("active"));
var notEqualsCondition = dbService.AttributeNotEquals("Role", new Primitive("guest"));
// Numeric comparisons
var greaterCondition = dbService.AttributeIsGreaterThan("Age", new Primitive(18L));
var lessOrEqualCondition = dbService.AttributeIsLessOrEqual("Score", new Primitive(100.0));
// Check if array contains element
var hasPermission = dbService.ArrayElementExists("Permissions", new Primitive("admin"));
// Check if array does not contain element
var noBlockedTag = dbService.ArrayElementNotExists("Tags", new Primitive("blocked"));
Access deeply nested properties using dot notation:
// Simple nested access
var nestedCondition = dbService.AttributeEquals("User.Email", new Primitive("john@example.com"));
// Deep nesting (multiple levels)
var deepCondition = dbService.AttributeIsGreaterThan("Account.Settings.Security.Level", new Primitive(5L));
// Nested arrays
var nestedArrayCondition = dbService.ArrayElementExists("Profile.Certifications", new Primitive("AWS"));
// Nested with all operations
await dbService.UpdateItemAsync(tableName, key, updateData,
conditions: dbService.AttributeEquals("User.Status", new Primitive("verified"))
.And(dbService.AttributeIsGreaterOrEqual("User.Account.Balance", new Primitive(100.0))));
Combine conditions using And() and Or():
// AND logic - all conditions must be true
var adminCondition = dbService.AttributeEquals("Role", new Primitive("admin"))
.And(dbService.AttributeEquals("Status", new Primitive("active")));
// OR logic - at least one condition must be true
var accessCondition = dbService.AttributeEquals("Role", new Primitive("admin"))
.Or(dbService.AttributeEquals("Role", new Primitive("moderator")));
// Complex nested logic
var complexCondition = dbService.AttributeEquals("Department", new Primitive("IT"))
.And(
dbService.AttributeIsGreaterThan("Experience", new Primitive(5L))
.Or(dbService.ArrayElementExists("Certifications", new Primitive("Senior")))
);
// Check array size
var minItemsCondition = dbService.AttributeIsGreaterOrEqual("size(Items)", new Primitive(3L));
// Nested array size check
var nestedSizeCondition = dbService.AttributeEquals("size(Project.Team.Members)", new Primitive(5L));
Array indexing syntax (e.g., array[0]) is not supported. Instead, use the dedicated array element condition methods:
// β INCORRECT - This will throw an ArgumentException
var wrongCondition = dbService.AttributeEquals("Tags[0]", new Primitive("admin"));
var wrongNestedCondition = dbService.AttributeEquals("User.Permissions[0]", new Primitive("read"));
// β
CORRECT - Use ArrayElementExists for checking array contents
var correctCondition = dbService.ArrayElementExists("Tags", new Primitive("admin"));
var correctNestedCondition = dbService.ArrayElementExists("User.Permissions", new Primitive("read"));
CrossCloudKit is designed to work consistently across all database providers (AWS DynamoDB, MongoDB, Google Cloud Datastore, and file-based storage). Array indexing semantics vary significantly across these platforms, and direct index access doesn't align with the distributed nature of NoSQL databases.
var key = new DbKey("id", new Primitive("user-123"));
// Atomically increment a counter
var newValue = await dbService.IncrementAttributeAsync(
"Users", key, "LoginCount", incrementValue: 1
);
if (newValue.IsSuccessful)
{
Console.WriteLine($"New login count: {newValue.Data}");
}
// Scan all items
var allUsers = await dbService.ScanTableAsync("Users");
// Scan with filter
var activeUsersFilter = dbService.AttributeEquals("Status", new Primitive("active"));
var activeUsers = await dbService.ScanTableWithFilterAsync("Users", activeUsersFilter);
// Paginated scan
var (items, nextToken, totalCount) = await dbService.ScanTablePaginatedAsync(
"Users", pageSize: 10
);
using CrossCloudKit.Interfaces.Classes;
// Initialize your services
var databaseService = new DatabaseServiceAWS(/* parameters */);
var fileService = new FileServiceAWS(/* parameters */);
var pubSubService = new PubSubServiceAWS(/* parameters */);
// Create backup service with daily backups at 1:00 AM UTC
var backupService = new DatabaseServiceBackup(
databaseService: databaseService,
fileService: fileService,
backupBucketName: "my-backup-bucket",
pubsubService: pubSubService,
cronExpression: "0 1 * * *", // Daily at 1:00 AM
timeZoneInfo: TimeZoneInfo.Utc,
backupRootPath: "database-backups/",
errorMessageAction: ex => Console.WriteLine($"Backup error: {ex.Message}")
);
// Every hour: "0 * * * *"
// Daily at 2:30 AM: "30 2 * * *"
// Weekly on Sundays at midnight: "0 0 * * 0"
// Monthly on the 1st at midnight: "0 0 1 * *"
// Every 6 hours: "0 */6 * * *"
// Get backup cursors
var backupCursors = backupService.GetBackupFileCursorsAsync().ToListAsync();
// Restore from a specific backup (e.g., most recent)
var latestBackup = backupCursors.Last();
var restoreResult = await backupService.RestoreBackupAsync(latestBackup);
if (restoreResult.IsSuccessful)
{
Console.WriteLine("Database restored successfully!");
}
else
{
Console.WriteLine($"Restore failed: {restoreResult.ErrorMessage}");
}
using CrossCloudKit.Interfaces.Classes;
// Initialize your services
var databaseService = new DatabaseServiceAWS(/* parameters */);
var fileService = new FileServiceAWS(/* parameters */);
var pubSubService = new PubSubServiceAWS(/* parameters */);
// Create backup service without automatic backups, for manual backups only
var backupService = new DatabaseServiceBackup(
databaseService: databaseService,
fileService: fileService,
backupBucketName: "my-backup-bucket",
pubsubService: pubSubService,
backupRootPath: "database-backups/",
errorMessageAction: ex => Console.WriteLine($"Backup error: {ex.Message}")
);
//Take backup
var result = await backupService.TakeBackupAsync();
if (result.IsSuccessful)
{
Console.WriteLine("Database backup was successful!");
}
else
{
Console.WriteLine($"Backup failed: {result.ErrorMessage}");
return;
};
//How to restore:
var restoreResult = await backupService.RestoreBackupAsync(result.Data);
if (restoreResult.IsSuccessful)
{
Console.WriteLine("Database restored successfully!");
}
else
{
Console.WriteLine($"Restore failed: {restoreResult.ErrorMessage}");
return;
}
using CrossCloudKit.Interfaces.Classes;
// Initialize your services
var fileService = new FileServiceAWS(/* parameters */);
var pubSubService = new PubSubServiceAWS(/* parameters */); //Needed for ensuring atomicity
var sourceDatabaseService = new DatabaseServiceAWS(/* parameters */);
var destinationDatabaseService = new DatabaseServiceGC(/* parameters */);
var result = await DatabaseServiceMigration.MigrateAsync(
sourceDatabaseService,
destinationDatabaseService,
fileService,
pubSubService, //Needed for ensuring atomicity of this operation
backupWorkBucketName: "my-tmp-bucket",
cleanUpSourceDatabaseAfterMigrate: false,
cleanUpDestinationDatabaseBeforeMigrate: false,
errorMessageAction: ex => Console.WriteLine($"Migration error: {ex.Message}"));
if (result.IsSuccessful)
{
Console.WriteLine("Database migration was successful!");
}
else
{
Console.WriteLine($"Migration failed: {result.ErrorMessage}");
}
webAppBuilder.Services.AddSingleton<IFileProvider>(
new FileServiceFileProvider(/**/);
// Get file metadata
var metadata = await fileService.GetFileMetadataAsync("my-bucket", "files/document.pdf");
if (metadata.IsSuccessful)
{
Console.WriteLine($"Size: {metadata.Data.Size} bytes");
Console.WriteLine($"Content-Type: {metadata.Data.ContentType}");
Console.WriteLine($"Last Modified: {metadata.Data.LastModified}");
}
// Set file tags
var tags = new Dictionary<string, string>
{
["department"] = "engineering",
["classification"] = "internal"
};
await fileService.SetFileTagsAsync("my-bucket", "files/document.pdf", tags);
// List files with pagination
var listResult = await fileService.ListFilesAsync("my-bucket", new ListFilesOptions
{
Prefix = "uploads/",
MaxResults = 100
});
foreach (var fileKey in listResult.Data.FileKeys)
{
Console.WriteLine($"File: {fileKey}");
}
// Delete entire folder
var deletedCount = await fileService.DeleteFolderAsync("my-bucket", "temp/");
Console.WriteLine($"Deleted {deletedCount.Data} files");
webAppBuilder.Services.AddSingleton<IDistributedCache>(
new MemoryServiceDistributedCache(
new MemoryServiceRedis(/**/),
new MemoryScopeLambda(() => "my-app-scope")
);
// Work with Redis lists
await memoryService.PushToListTailAsync(scope, "queue", new[]
{
new Primitive("task1"),
new Primitive("task2")
});
// Pop from list
var task = await memoryService.PopFirstElementOfListAsync(scope, "queue");
Console.WriteLine($"Processing: {task.Data?.AsString}");
// Check if list contains value
var contains = await memoryService.ListContainsAsync(scope, "queue", new Primitive("task2"));
// Set expiration time
await memoryService.SetKeyExpireTimeAsync(scope, TimeSpan.FromHours(1));
// Conditional set (only if not exists)
var wasSet = await memoryService.SetKeyValueConditionallyAsync(
scope, "initialized", new Primitive("true")
);
// Get all keys in scope
var keys = await memoryService.GetKeysAsync(scope);
Console.WriteLine($"Keys in scope: {string.Join(", ", keys.Data!)}");
// Set up file notifications using PubSub integration
var notificationId = await fileService.CreateNotificationAsync(
bucketName: "my-bucket",
topicName: "file-events",
pathPrefix: "uploads/",
eventTypes: new[] { FileNotificationEventType.ObjectCreated, FileNotificationEventType.ObjectDeleted },
pubSubService: pubSubService
);
// Subscribe to file events
await pubSubService.SubscribeAsync("file-events", async (topic, message) =>
{
Console.WriteLine($"File event: {message}");
// Process file upload/deletion events
});
All Basic service providers (Database, File, Memory, Vector, PubSub) automatically register with an embedded HTTP debug dashboard β zero configuration required.
// Just create any Basic service β the debug panel starts automatically
await using var memoryService = new MemoryServiceBasic();
await using var vectorService = new VectorServiceBasic();
var dbService = new DatabaseServiceBasic("myapp", memoryService);
// Open http://localhost:57765 in your browser
| Environment Variable | Default | Description |
|---|---|---|
CROSSCLOUDKIT_DEBUG_PANEL_PORT |
57765 |
Port for the dashboard |
CROSSCLOUDKIT_DEBUG_PANEL_DISABLED |
false |
Set to true to disable entirely |
Add a [ModuleInitializer] in your test project to prevent test runs from spawning debug panels:
using System.Runtime.CompilerServices;
internal static class ModuleInit
{
[ModuleInitializer]
internal static void DisableDebugPanel()
{
Environment.SetEnvironmentVariable("CROSSCLOUDKIT_DEBUG_PANEL_DISABLED", "true");
}
}
CrossCloudKit uses a unified Primitive system that seamlessly maps across all cloud providers:
// String values
var stringKey = new Primitive("hello-world");
// Numeric values
var integerKey = new Primitive(12345L);
var doubleKey = new Primitive(123.45);
// Binary data
var binaryKey = new Primitive(new byte[] { 1, 2, 3, 4 });
// Type-safe access
Console.WriteLine(stringKey.AsString);
Console.WriteLine(integerKey.AsInteger);
Console.WriteLine(doubleKey.AsDouble);
Console.WriteLine(Convert.ToBase64String(binaryKey.AsByteArray));
The library includes comprehensive integration tests for all providers:
# Run all tests
dotnet test
# Run tests for specific service type
dotnet test CrossCloudKit.Database.AWS.Tests
dotnet test CrossCloudKit.Database.Mongo.Tests
dotnet test CrossCloudKit.Database.GC.Tests
dotnet test CrossCloudKit.Database.Basic.Tests
dotnet test CrossCloudKit.File.AWS.Tests
dotnet test CrossCloudKit.File.GC.Tests
dotnet test CrossCloudKit.File.S3Compatible.Tests
dotnet test CrossCloudKit.File.Basic.Tests
dotnet test CrossCloudKit.PubSub.AWS.Tests
dotnet test CrossCloudKit.PubSub.GC.Tests
dotnet test CrossCloudKit.PubSub.Redis.Tests
dotnet test CrossCloudKit.PubSub.Basic.Tests
dotnet test CrossCloudKit.Memory.Redis.Tests
dotnet test CrossCloudKit.Memory.Basic.Tests
dotnet test CrossCloudKit.LLM.OpenAI.Tests
dotnet test CrossCloudKit.LLM.Basic.Tests
dotnet test CrossCloudKit.Vector.Basic.Tests
dotnet test CrossCloudKit.Vector.Qdrant.Tests
# Bridge / unit tests (no external services required)
dotnet test CrossCloudKit.Interfaces.Tests
Tests support environment variables for real cloud service integration:
AWS Services (DynamoDB, S3, SNS/SQS Hybrid):
AWS_ACCESS_KEY=your-key
AWS_SECRET_KEY=your-secret
AWS_REGION=us-east-1
MongoDB:
MONGODB_CONNECTION_STRING=mongodb+srv://user:password@your-host/
Google Cloud Services (Datastore, Storage, Pub/Sub):
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_APPLICATION_CREDENTIALS_BASE64=your-base64-encoded-service-account
GOOGLE_CLOUD_TEST_BUCKET=test-bucket-name
Redis (Memory & PubSub):
REDIS_HOST=localhost
REDIS_PORT=6379
# Optional:
REDIS_USER=your-redis-user
REDIS_PASSWORD=your-redis-password
REDIS_ENABLE_SSL=true
LLM Services:
# OpenAI / any OpenAI-compatible endpoint (e.g. Ollama)
OPENAI_BASE_URL=http://localhost:11434/v1
OPENAI_API_KEY= # leave empty for Ollama
OPENAI_MODEL=qwen2.5:14b # completion model
OPENAI_EMBEDDING_MODEL=nomic-embed-text:v1.5 # embedding model (optional, falls back to MODEL)
# LLM.Basic β completions work out of the box with the bundled SmolLM2-135M model.
# Set this only to override with a different GGUF model:
LLM_BASIC_MODEL_PATH=/path/to/custom-model.gguf
Qdrant (Vector Database):
QDRANT_HOST=localhost
QDRANT_PORT=6334
# Optional:
QDRANT_API_KEY=your-api-key
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Application Layer β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β CrossCloudKit.Interfaces (Unified API + Bridge Layer) β
β IDatabaseService | IFileService | IPubSubService | IMemoryService β
β ILLMService | IVectorService | LLMVectorExtensions (bridge) β
ββββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββββββ¬βββββββββββββββββββββββββββ€
β Database β File β PubSub β Memory β LLM β Vector DB β
β Services β Storage β Services β Services β Services β Services β
ββββββββββββΌβββββββββββΌβββββββββββΌβββββββββββΌβββββββββββββββΌβββββββββββββββββββββββββββ€
β AWS β AWS S3 β AWS β Redis β OpenAI-compatβ In-memory (Basic) β
β DynamoDB β Google β SNS/SQS β (Lists, β (Ollama, β (zero deps, unit tests) β
β MongoDB β Storage β Google β KV,Mutex)β Groq, Azure, β Qdrant (gRPC) β
β Google β S3-compatβ Pub/Sub β Basic β Bedrock, OAI)β (production) β
β Datastoreβ Basic β Redis β (Cross- β Local CPU β β
β Basic β (local) β Basic β Process) β (LLamaSharp +β β
β β β β β SmolLM2-135M)β β
ββββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββββββ΄βββββββββββββββββββββββββββ€
β CrossCloudKit.Basic.DebugPanel β
β (Embedded HTTP dashboard, SSE, data browsing, dead-process cleanup) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β CrossCloudKit.Utilities.Common β
β (Primitive, OperationResult, etc.) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The Basic File Service supports HTTP-based signed URLs through ASP.NET Core endpoint registration. You can see FileServiceBasicIntegrationTest.cs for an example of how to set up the endpoint for signed urls to work with asp.net core.
// Switch between providers seamlessly
IDatabaseService dbService = useCloud switch
{
"aws" => new DatabaseServiceAWS(/*Parameters*/),
"mongo" => new DatabaseServiceMongoDB(/*Parameters*/),
"gcp" => new DatabaseServiceGC(/*Parameters*/),
_ => new DatabaseServiceBasic(/*Parameters*/) // Local fallback
};
IFileService fileService = useCloud switch
{
"aws" => new FileServiceAWS(/*Parameters*/),
"gcp" => new FileServiceGC(/*Parameters*/),
"s3" => new FileServiceS3Compatible(/*Parameters*/),
_ => new FileServiceBasic(/*Parameters*/) // Local fallback
};
// Use Basic implementations for development or single-machine multi-process deployments
IMemoryService memoryService = useRedis
? new MemoryServiceRedis(/*Parameters*/) : new MemoryServiceBasic(/*Parameters*/);
IPubSubService pubSubService = useCloud
? new PubSubServiceAWS(/*Parameters*/) : new PubSubServiceBasic(/*Parameters*/);
// Service registry pattern
public class CloudServiceRegistry
{
public IDatabaseService Database { get; }
public IFileService FileStorage { get; }
public IPubSubService Messaging { get; }
public IMemoryService Cache { get; }
public CloudServiceRegistry(IConfiguration config)
{
Database = CreateDatabaseService(config);
FileStorage = CreateFileService(config);
Messaging = CreatePubSubService(config);
Cache = CreateMemoryService(config);
}
}
// Complete event-driven workflow
public class OrderProcessingService
{
private readonly IDatabaseService _db;
private readonly IFileService _files;
private readonly IPubSubService _pubsub;
private readonly IMemoryService _cache;
public async Task ProcessOrderAsync(Order order) {
// Store order in database
await _db.PutItemAsync("orders", "id", new Primitive(order.Id), JObject.FromObject(order));
// Generate receipt and store in file storage
var receipt = GenerateReceipt(order); await _files.UploadFileAsync(receipt, "receipts", $"{order.Id}.pdf");
// Cache order status
var cacheScope = new MemoryScopeLambda(() => $"order:{order.Id}");
await _cache.SetKeyValuesAsync(cacheScope, new[]
{
new KeyValuePair<string, Primitive>("status", new Primitive("processing"))
});
// Publish order event await
_pubsub.PublishAsync("order-events", JsonConvert.SerializeObject(new { OrderId = order.Id, Status = "processing" })); }}
CrossCloudKit ships with pre-built instruction files that teach AI coding assistants (GitHub Copilot, Cursor, Windsurf, and others) the correct API patterns, types, anti-patterns, and provider constructors. These files are automatically loaded by each IDE when you work inside the repository.
| IDE / Tool | File | Auto-loaded? |
|---|---|---|
| GitHub Copilot (VS Code, VS, JetBrains) | β Yes | |
| Cursor | β
Yes (alwaysApply: true) |
|
| Windsurf | β
Yes (trigger: always_on) |
If you consume CrossCloudKit via NuGet (rather than cloning this repo), the AI instruction files aren't automatically present in your project. To enable AI assistance in your own codebase:
Copy the appropriate file from this repository into your project:
# GitHub Copilot β place in your repo's .github/ folder
mkdir -p .github
curl -o .github/copilot-instructions.md \
https://raw.githubusercontent.com/bkio/CrossCloudKit/main/.github/copilot-instructions.md
# Cursor β place in your repo's .cursor/rules/ folder
mkdir -p .cursor/rules
curl -o .cursor/rules/crosscloudkit.mdc \
https://raw.githubusercontent.com/bkio/CrossCloudKit/main/.cursor/rules/crosscloudkit.mdc
# Windsurf β place in your repo's .windsurf/rules/ folder
mkdir -p .windsurf/rules
curl -o .windsurf/rules/crosscloudkit.md \
https://raw.githubusercontent.com/bkio/CrossCloudKit/main/.windsurf/rules/crosscloudkit.md
Commit the file to your repository so the entire team benefits.
Other IDEs: Any AI assistant that supports markdown-based project rules can use the same content. Copy any of the files above and place it according to your IDE's convention:
.junie/guidelines.md.clinerulesCONVENTIONS.mdAGENTS.md (recognized by Cursor, Windsurf, and others)Tip: The instruction files are identical in content β only the frontmatter/header differs per IDE. You only need one copy for each IDE you use.
This project is licensed under the MIT License - see the file for details.
Made with β€οΈ by the Burak Kara
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 1 NuGet packages that depend on CrossCloudKit.File.AWS:
| Package | Downloads |
|---|---|
|
CrossCloudKit.File.S3Compatible
Package Description |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2026.4.22.71 | 116 | 4/22/2026 |
| 2026.4.20.68 | 119 | 4/20/2026 |
| 2026.4.20.67 | 117 | 4/20/2026 |
| 2026.4.19.63 | 112 | 4/19/2026 |
| 2026.4.16.60 | 122 | 4/16/2026 |
| 2026.4.16.59 | 117 | 4/16/2026 |
| 2026.4.14.56 | 125 | 4/14/2026 |
| 2026.4.13.53 | 129 | 4/13/2026 |
| 2025.10.8.50 | 245 | 10/8/2025 |
| 2025.10.7.48 | 229 | 10/7/2025 |
| 2025.10.6.42 | 229 | 10/6/2025 |
| 2025.10.5.41 | 230 | 10/5/2025 |
| 2025.10.3.39 | 192 | 10/3/2025 |
| 2025.10.2.37 | 238 | 10/2/2025 |
| 2025.9.22.33 | 246 | 9/22/2025 |
| 2025.9.21.32 | 236 | 9/21/2025 |
| 2025.9.18.29 | 338 | 9/18/2025 |
| 2025.9.13.25 | 190 | 9/13/2025 |
| 2025.9.11.24 | 220 | 9/11/2025 |
| 2025.9.5.23 | 209 | 9/5/2025 |