 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			76 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			76 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // The storage package contains interfaces for storage systems, and functions for using them.
 | |
| //
 | |
| // These are very low-level storage primitives.
 | |
| // The interfaces here deal only with raw keys and raw binary blob values.
 | |
| //
 | |
| // In IPLD, you can often avoid dealing with storage directly yourself,
 | |
| // and instead use linking.LinkSystem to handle serialization, hashing, and storage all at once.
 | |
| // (You'll hand some values that match interfaces from this package to LinkSystem when configuring it.)
 | |
| // It's probably best to work at that level and above as much as possible.
 | |
| // If you do need to interact with storage more directly, the read on.
 | |
| //
 | |
| // The most basic APIs are ReadableStorage and WritableStorage.
 | |
| // When writing code that works with storage systems, these two interfaces should be seen in almost all situations:
 | |
| // user code is recommended to think in terms of these types;
 | |
| // functions provided by this package will accept parameters of these types and work on them;
 | |
| // implementations are expected to provide these types first;
 | |
| // and any new library code is recommended to keep with the theme: use these interfaces preferentially.
 | |
| //
 | |
| // Users should decide which actions they want to take using a storage system,
 | |
| // find the appropriate function in this package (n.b., package function -- not a method on an interface!
 | |
| // You will likely find one of each, with the same name: pick the package function!),
 | |
| // and use that function, providing it the storage system (e.g. either ReadableStorage, WritableStorage, or sometimes just Storage)
 | |
| // as a parameter.
 | |
| // That function will then use feature-detection (checking for matches to the other,
 | |
| // more advanced and more specific interfaces in this package) and choose the best way
 | |
| // to satisfy the request; or, if it can't feature-detect any relevant features,
 | |
| // the function will fall back to synthesizing the requested behavior out of the most basic API.
 | |
| // Using the package functions, and letting them do the feature detection for you,
 | |
| // should provide the most consistent user experience and minimize the amount of work you need to do.
 | |
| // (Bonus: It also gives us a convenient place to smooth out any future library migrations for you!)
 | |
| //
 | |
| // If writing new APIs that are meant to work reusably for any storage implementation:
 | |
| // APIs should usually be designed around accepting ReadableStorage or WritableStorage as parameters
 | |
| // (depending on which direction of data flow the API is regarding).
 | |
| // and use the other interfaces (e.g. StreamingReadableStorage) thereafter internally for feature detection.
 | |
| // For APIs which may sometimes be found relating to either a read or a write direction of data flow,
 | |
| // the Storage interface may be used in order to define a function that should accept either ReadableStorage or WritableStorage.
 | |
| // In other words: when writing reusable APIs, one should follow the same pattern as this package's own functions do.
 | |
| //
 | |
| // Similarly, implementers of storage systems should always implement either ReadableStorage or WritableStorage first.
 | |
| // Only after satisfying one of those should the implementation then move on to further supporting
 | |
| // additional interfaces in this package (all of which are meant to support feature-detection).
 | |
| // Beyond one of the basic two, all the other interfaces are optional:
 | |
| // you can implement them if you want to advertise additional features,
 | |
| // or advertise fastpaths that your storage system supports;
 | |
| // but you don't have implement any of those additional interfaces if you don't want to,
 | |
| // or if your implementation can't offer useful fastpaths for them.
 | |
| //
 | |
| // Storage systems as described by this package are allowed to make some interesting trades.
 | |
| // Generally, write operations are allowed to be first-write-wins.
 | |
| // Furthermore, there is no requirement that the system return an error if a subsequent write to the same key has different content.
 | |
| // These rules are reasonable for a content-addressed storage system, and allow great optimizations to be made.
 | |
| //
 | |
| // Note that all of the interfaces in this package only use types that are present in the golang standard library.
 | |
| // This is intentional, and was done very carefully.
 | |
| // If implementing a storage system, you should find it possible to do so *without* importing this package.
 | |
| // Because only standard library types are present in the interface contracts,
 | |
| // it's possible to implement types that align with the interfaces without refering to them.
 | |
| //
 | |
| // Note that where keys are discussed in this package, they use the golang string type --
 | |
| // however, they may be binary.  (The golang string type allows arbitrary bytes in general,
 | |
| // and here, we both use that, and explicitly disavow the usual "norm" that the string type implies UTF-8.
 | |
| // This is roughly the same as the practical truth that appears when using e.g. os.OpenFile and other similar functions.)
 | |
| // If you are creating a storage implementation where the underlying medium does not support arbitrary binary keys,
 | |
| // then it is strongly recommend that your storage implementation should support being configured with
 | |
| // an "escaping function", which should typically simply be of the form `func(string) string`.
 | |
| // Additional, your storage implementation's documentation should also clearly describe its internal limitations,
 | |
| // so that users have enough information to write an escaping function which
 | |
| // maps their domain into the domain your storage implementation can handle.
 | |
| package storage
 | |
| 
 | |
| // also note:
 | |
| // LinkContext stays *out* of this package.  It's a chooser-related thing.
 | |
| // LinkSystem can think about it (and your callbacks over there can think about it), and that's the end of its road.
 | |
| // (Future: probably LinkSystem should have SetStorage and SetupStorageChooser methods for helping you set things up -- where the former doesn't discuss LinkContext at all.)
 |