-
Notifications
You must be signed in to change notification settings - Fork 0
Feature: Storage Anchor #164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Introduces a new Storage Anchor service with HTTP server + client support, including path-based object storage, quota reporting, namespace validation, and public access via pre-signed URLs. It also extends the resolver metadata model and enhances EncryptedContainer with structured error handling and optional signing.
Changes:
- Added Storage Anchor server routes (put/get/delete/search/quota/public) with quota/validator hooks and service metadata publishing.
- Added Storage Anchor client/provider for discovery via resolver, CRUD operations, search/quota, and pre-signed public URLs.
- Extended core libraries: resolver types/lookup, error deserialization mapping, and
EncryptedContainererror/signing support (with tests).
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/services/storage/server.ts | Implements Storage Anchor HTTP API endpoints and publishes storage service metadata |
| src/services/storage/server.test.ts | Adds basic server/serviceMetadata and error-path tests |
| src/services/storage/lib/validators.ts | Adds namespace validator framework + built-in icon validator |
| src/services/storage/lib/validators.test.ts | Tests validator matching and icon validation logic |
| src/services/storage/common.ts | Defines storage types, requests/responses, signing data, and storage-specific errors |
| src/services/storage/common.test.ts | Adds path utility tests and an in-memory backend for test usage |
| src/services/storage/client.ts | Adds resolver-driven client/provider with signed requests and public URL generation |
| src/services/storage/client.test.ts | Adds integration-style tests for provider discovery and CRUD/search/quota/public flows |
| src/lib/utils/tests/node.ts | Adds helper to publish resolver metadata in tests |
| src/lib/resolver.ts | Extends resolver metadata schema and lookup logic for storage services |
| src/lib/resolver.test.ts | Refactors to reuse shared resolver-metadata helper |
| src/lib/error.ts | Registers storage service errors for JSON deserialization |
| src/lib/encrypted-container.ts | Adds structured error type/codes and optional signing (v3) support |
| src/lib/encrypted-container.test.ts | Updates tests for buffer conversions + adds error/signing coverage |
| src/client/index.ts | Exposes Storage client entrypoint alongside other service clients |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 15 out of 16 changed files in this pull request and generated 6 comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 15 out of 16 changed files in this pull request and generated 5 comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 16 out of 17 changed files in this pull request and generated 7 comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 16 out of 17 changed files in this pull request and generated 1 comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 16 out of 17 changed files in this pull request and generated 6 comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this file changing while package.json is not in this changeset ?
| * Interface for atomic storage operations. | ||
| * Provides the same operations as StorageBackend but within an atomic scope. | ||
| */ | ||
| export interface StorageAtomicInterface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it will be a VERY difficult interface to implement for a real world provider, and I'm not sure we need an atomic interface for multiple storage operations.
What's the thinking behind requiring every provider to take a snapshot of what a storage system looks like at a given moment so that a search doesn't return any newer documents, for example ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quota management was the original issue. You could exceed or be prevented from using the limit under certain circumstances.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like there should be tests for this then ?
src/services/storage/server.ts
Outdated
|
|
||
| // Validate encrypted container if needed | ||
| if (needsAnchorDecryption) { | ||
| if (!anchorAccount?.hasPrivateKey) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could check this at startup
src/services/storage/server.ts
Outdated
| }; | ||
|
|
||
| // GET /api/object - Retrieve an object | ||
| routes['GET /api/object'] = async function(_params, _postData, _headers, url) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally the resource would be part of the URL pathname and not as query parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
routeMatch does not support wildcards. Suggestions?
src/services/storage/server.ts
Outdated
| const signatureBuffer = Buffer.from(signature, 'base64'); | ||
| const messageBuffer = Buffer.from(message, 'utf-8'); | ||
|
|
||
| const validSig = ownerAccount.verify( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use the utils/signing library ?
src/services/storage/server.ts
Outdated
| } | ||
|
|
||
| // Decrypt using anchor account | ||
| if (!anchorAccount?.hasPrivateKey) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, this could be checked at startup instead of at random places throughout the code
src/services/storage/server.ts
Outdated
| } | ||
|
|
||
| // Decode base64 data to get actual stored object size | ||
| const data = Buffer.from(request.data, 'base64'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would the data we're asking to be stored come to us in base64 encoding ? It should be the body of the request which can be encoded safely in a more efficient way
src/services/storage/client.test.ts
Outdated
| }) => Promise<StorageObjectMetadata>; | ||
| } | ||
|
|
||
| type ClientTestFn = (ctx: ClientTestContext) => Promise<void>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're not starving for space, we can use real words like context and Function when writing a type that is meant to describe what things are...
src/services/storage/client.test.ts
Outdated
| const makePath = (relativePath: string): StoragePath => | ||
| makeStoragePath(account.publicKeyString.get(), relativePath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const makePath = (relativePath: string): StoragePath => | |
| makeStoragePath(account.publicKeyString.get(), relativePath); | |
| const makePath = function(relativePath: string): StoragePath { | |
| return(makeStoragePath(account.publicKeyString.get(), relativePath)); | |
| }; |
src/services/storage/client.test.ts
Outdated
| /** | ||
| * Helper to run a test with a fully configured client and provider. | ||
| */ | ||
| async function withClient(seed: string | ArrayBuffer, fn: ClientTestFn, options: WithClientOptions = {}): Promise<void> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to describe the method we're calling as fn -- for one, we already know its type and shape an Fn (or as some might say, a function) from the type it can have a better name, one meant for humans who are the only ones who care about names.
src/services/storage/client.test.ts
Outdated
|
|
||
| // #endregion | ||
|
|
||
| describe('Storage Client - Provider Discovery', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| describe('Storage Client - Provider Discovery', () => { | |
| describe('Storage Client - Provider Discovery', function() { |
src/services/storage/client.test.ts
Outdated
| const path = makePath('test.txt'); | ||
|
|
||
| // PUT | ||
| const putResult = await provider.put(path, testData, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the client should have a beginSession() method with a given provider which lets you have a session that has some state like:
- account
- working directory
- default visibility (optional)
|



Summary
This PR introduces a new storage anchor service:
Key Changes
Encrypted Container (src/lib/encrypted-container.ts)
Storage Service (src/services/storage/)