Skip to the content
Krizaka TypeScript
Krizaka TypeScript

@krizaka/storage

Files behind one port: content-hashed names, signed and public links; an HTTP zone, any S3-compatible bucket or a folder, chosen by the admin; bytes checked by magic number.

Install

bashTerminal
npm install @krizaka/storage

Also published as subpath exports: @krizaka/storage/node, @krizaka/storage/settings, @krizaka/storage/resolve, @krizaka/storage/testkit, @krizaka/storage/provider-admin, @krizaka/storage/opendal.

Files behind one port for every Krizaka product. The port is named by what it does (ObjectStore), the provider is a value the admin chooses. No vendor SDK (plain HTTP through an injected fetch), no environment variable, no wall clock.

  • The port. put, get, head, delete, list(prefix), signedUrl(key, ttl) for a private zone, publicUrl(key) when a pull zone or CDN exists.
  • Adapters. remoteStore (an HTTP zone with the shape of Bunny Storage: AccessKey header, PUT/GET/DELETE, a directory listed by its path with a trailing slash, a pull zone with token-signed links), bucketStore (any S3-compatible service: Signature V4, presigned GET, ListObjectsV2, no SDK), folderStore (a folder, for development, tests and the Mac apps; a key never leaves the root), memoryObjectStore. All run objectStoreContract (@krizaka/storage/testkit).
  • Rules every upload obeys (createStorage): the size limit is passed in, the prefix must be declared in a defineLayout (an unknown prefix is refused), the bytes are checked by magic number (sniffType: png, jpeg, gif, webp, pdf, mp4, webm, svg), never by the extension, and a declared type that contradicts the bytes is refused. The stored name is content-hashed, <name>.<sha10>.<ext>, so a URL never changes content.
  • The admin chooses (@krizaka/storage/settings): storage.provider (local, http, s3), storage.maxMegabytes, storage.signedUrlSeconds, and the non-secret coordinates (endpoint, region, bucket, zone, prefix, pullHostname, publicBaseUrl). Keys stay in the environment; storeFromSettings takes them as parameters and refuses a provider chosen without what it needs, instead of falling back to another. An admin-typed S3 endpoint goes through checkEndpoint (https only, no credentials, no local or private address).
  • Encryption at rest, the vault, regions and where. encryptedStore(inner, key) (AES-256-GCM over any ObjectStore; the key is a parameter; the provider holds ciphertext; no link is handed out), seal/open (credentials in the database, v1. envelope), COUNTRY_OF_REGION (the one region table: an unlisted region is never a country; residencyOfConfig says « canada » only when the region and the endpoint agree, never for a Bunny-shaped zone), where(kind, active, { fixed, devOnly }) and storageNotice (the sentence key of the info panel, from the configuration).
  • Admin over environment (@krizaka/storage/resolve). resolveStorage(env, admin) serves every kind of data (documents, avatars: pass each its own DeploymentStorage): the admin's storage.* wins, the environment gives the secrets and the fallback when the admin left local. If the environment says none and the admin turns keeping on, it throws StorageError STORAGE_KEEPING_OFF (call it when saving the setting to refuse early): keeping is turned on in the environment, never by a setting alone. openResolved builds the store, encrypted when a key is given.
  • Provider registry (ADR-005). PROVIDER_KINDS is the table of kinds as data (bunny, s3 with presets DigitalOcean Spaces, AWS S3, OVHcloud and generic, dropbox, reserved google-drive/onedrive, folder, device): scopes, runtimes, devOnly, capabilities, coordinate and credential field schemas. openProvider(kind, coordinates, credentials, { runtime, environment, fetch, clock }) (/node) is the only factory: it refuses (STORAGE_BAD_CONFIG) devOnly kinds in prod, folder outside dev, an unknown or unimplemented kind and a missing field, and never falls back. It returns the store and a ResidencyLabel (PROVINCE_OF_REGION: « Canada (Québec) », « Canada (Ontario) »…) for the screen to show before saving. testConnection (probe write, read, compare, delete) and checkEndpointDns (the written form plus the DNS answer: a private address is refused) serve every product.
  • The registry opens the OpenDAL kinds and private kinds (ADR-005 amendment, krizaka-ts#239). google-drive and onedrive are implemented at user scope (through opendalStore and CLOUD_CONNECTORS: the access token is renewed from the sealed refresh token, a rotated one comes back through OpenContext.onRotate and is sealed again by the service); azblob, gcs, b2 and webdav at admin scope (their field names are OpenDAL's own options; a typed url field passes checkEndpoint and the DNS check). openProvider is now asynchronous. Each kind maps to its adapter in a table (BUILT_IN_ADAPTERS), never a branch. A private package adds a kind without touching the core: defaultProviderRegistry.with({ kind, open }) (or createProviderRegistry({ kinds, adapters })) returns a new registry; pass it to openProvider(…, registry) and createProviderService({ registry }). A duplicate name is refused, a secret may not be a coordinate, a credential must be a secret field (sealed), and the runtime, devOnly, required-field and url rules apply to private kinds as to the built-in ones.
  • Keyring is a port ({ keyId, wrap, unwrap, openLegacy? }). localKeyring({ keyId, key, ring }) is the in-process default (the old Keyring shape { keys, legacy } is now LocalKeys); sealWith, openWith and rewrapWith take any keyring, so a KMS (AWS KMS ca-central-1, OVHcloud KMS, Vault transit) wraps the data key and the key never reaches the app. createProviderService({ vault }) takes either the old { keyId, key, ring } or a Keyring. The stored v2.<key_id>… format is unchanged: moving to a KMS is rotateVaultKey, not a re-entry.
  • Envelope vault. sealEnvelope/openEnvelope/rewrapEnvelope: a data key per sealed value, wrapped by the key-encryption key (MEDIA_VAULT_KEY) named by a key_id (v2.<key_id>.<wrapped>.<sealed>); v1. values still open with the legacy key; rotating the key re-wraps the data keys only.
  • Providers in the database (@krizaka/storage/provider-admin, ADR-005 slice 2). storageSchema (tables storage.provider and storage.default_provider), dbProviderStore/memoryProviderStore (contract: providerStoreContract), and createProviderService: create (a draft, secrets sealed in an envelope), test (only a green test verifies), setDefault (verified only; the previous default becomes retiring), changeKey (the candidate is tested first), disable/retire (refused while the product still holds objects there), rotateVaultKey, bootstrap (imports the old MEDIA_* environment once as an active provider, source: env). Secrets come back as set/not set only; every change goes to the injected audit with secret fields as changed. moveBatch (get, check the SHA-256, put, read back and compare, then switch the row in the product transaction; failures are returned, never switched) and readDual (the row provider, then the retiring ones) are the move and the dual read.
  • Users' own clouds (ADR-005 slice 4). dropboxStore (dropbox kind, user scope): App folder only, upload sessions above 150 MB, no signed or public link (Dropbox links outlive our 900 s cap, so the application streams). pkcePair, dropboxAuthorizeUrl, exchangeDropboxCode, refreshDropboxToken, revokeDropbox are the OAuth 2 PKCE steps; the refresh token is sealed like any credential, and a revoked or expired one answers STORAGE_DISCONNECTED (nothing is moved or deleted). A user's own S3 is the same s3 kind at user:<id> scope, with the DNS check of its endpoint.
  • Every classic cloud through libraries (ADR-005 amendment, krizaka-ts#235). opendalStore (@krizaka/storage/opendal) is one adapter over Apache OpenDAL for Google Drive, OneDrive, Dropbox, Azure Blob, GCS, B2, Swift and WebDAV. Only the services of OPENDAL_SERVICES open; fs/memory need allowLocal; a typed endpoint passes checkEndpoint and the DNS check first. OpenDAL's errors map onto STORAGE_* (PermissionDenied on an OAuth cloud → STORAGE_DISCONNECTED), and readStream(key) streams large files. opendal is an optional peer (a native module of 59–82 MB, glibc ≥ 2.38 or musl), loaded on first use. CLOUD_CONNECTORS (/node) is the table of user clouds (Dropbox, Google Drive, OneDrive: endpoints, narrowest scope, revocation style). startConnection, finishConnection, refreshConnection, revokeConnection run Arctic's authorization code + PKCE S256, and connectionAccessToken feeds opendalStore a fresh access token and hands a rotated refresh token back to be resealed. dropboxStore and its helpers are superseded and stay until the products move.
  • Signatures. SigV4 is checked against AWS's published vectors. The token form of the HTTP zone's signed links (SHA-256 of key + path + expires, URL-safe base64) is the one Yomizaka runs against Bunny's pull zones; Bunny's documentation publishes no expected output for it.
import { createStorage, defineLayout, folderStore } from "@krizaka/storage";
import { limitsOf, readStorageSettings, storeFromSettings } from "@krizaka/storage/settings";

const settings = await readStorageSettings((key) => service.get(key, ctx));
const storage = createStorage({
  store: storeFromSettings(settings, { accessKeyId: env.MEDIA_STORAGE_ACCESS_KEY_ID, secretAccessKey: env.MEDIA_STORAGE_SECRET }, { fetch, clock }),
  layout: defineLayout(["people/", "site/home/"]),
  limits: () => readStorageSettings(read).then(limitsOf),
});
const { key, url } = await storage.upload({ prefix: "people/", name: "ana.jpg", bytes, allow: ["image/jpeg", "image/png"], declaredType: file.type });

Apache-2.0.

Exports

The 49 names the package exports, read from its types.

NameKindWhat it is
assertKeyfunctionA key is a/b/name.ext: no empty segment, no . or .., no leading or trailing slash, no backslash, no control character.
checkEndpointfunctionAn endpoint an administrator typed (their own bucket) is called by our server: it must not reach inside our network.
Clocktype
COUNTRY_OF_REGIONconstantThe country of each provider region (data: a new region is a line here, one table for every product, ADR-010).
countryOffunctionThe ISO country of a provider region, or undefined when the region is not mapped.
defineLayoutfunction
describeStorefunctionThe descriptor of a configured store, from its configuration alone (see residencyOfConfig: a Bunny-shaped zone is dev only and never Canadian).
FetchLiketypeThe part of fetch the adapters use: inject the global one (fetch), or a fake in a test.
FieldSchematype
LayouttypeThe prefixes a zone allows, as data: an unknown prefix is refused.
memoryObjectStorefunctionAn in-memory store, for unit tests and demos: the behaviour of the others (the contract kit runs on all).
NoticeKeytype
ObjectInfotype
ObjectStoretypeOne place files live.
Presettype
presetEndpointfunctionThe endpoint a preset gives for a region, or null for the generic preset (typed by the person).
PROVIDER_KINDSconstant
providerKindfunction
ProviderKindtype
ProviderKindNametypeThe kinds this package ships.
ProviderRuntimetypeserver = a deployed web BFF, dev = a developer's machine or CI, device = a mobile or macOS client.
ProviderScopetype
Provincetype
PROVINCE_OF_REGIONconstantThe province of a Canadian region.
provinceOffunction
Residencytype
residencyLabelfunctionThe label of a configured store: Canada only when region and endpoint agree (residencyOfConfig); the province from the region table.
ResidencyLabeltypeWhere the files would live, as the screen says it before saving: key for the product's messages, text the English sentence.
residencyOffunctionCanada only when the table says so; an unmapped region is « elsewhere », never a guess in Canada's favour.
residencyOfConfigfunctionResidency of a configured store: the region AND the endpoint have to agree.
S3_PRESETSconstant
sniffTypefunctionThe content type the BYTES say they are, by magic number, never by the file name: png, jpeg, gif, webp, pdf, mp4, webm and svg.
STORAGE_CODEconstantThe refusals of this package: a stable code, and the HTTP status a route would answer.
StorageCodetype
StorageErrorclass
storageNoticefunctionWhich sentence says where this object lives (« Not kept », « Kept: Krizaka (Canada, encrypted) »…), with its variables; the product's messages hold the words.
STORE_KINDconstantThe two kinds every product shares; the others are the product's.
StoreDescriptortypeWhere objects live, as the product says it: computed from the configuration, never typed.
StoredObjecttype
StoreOwnertypeWhose store it is: Krizaka's (the deployment's), the workspace's own, or the person's device.
SweepOptionstype
sweepOrphansfunctionDeletes the files under prefix that isReferenced does not know: the leftovers of an upload whose row was rolled back, of a purge interrupted between the…
SweepResulttype
testConnectionfunctionThe connection test of a store: writes a small probe object, reads it back and compares, then deletes it, the three things a kept file needs.
TestSteptype
trimSlashesfunctionTrims slashes at the ends without a regular expression (a long run of slashes must not be quadratic).
typeOfNamefunctionThe content type a file name suggests, for stores that do not keep one.
wherefunctionWhere an object lives, from the store kind recorded with it and the store configured now.
WhereOptionstype

@krizaka/storage/node

NameKindWhat it is
AdapterInputtypeWhat an adapter receives: values already checked by the registry (required fields present, url fields passed checkEndpoint).
assertKeyfunctionA key is a/b/name.ext: no empty segment, no . or .., no leading or trailing slash, no backslash, no control character.
bucketStorefunctionAny S3-compatible service (Spaces, a customer's bucket, MinIO) over plain HTTP: path-style URLs and Signature V4, no SDK.
BucketStoreOptionstype
checkEndpointfunctionAn endpoint an administrator typed (their own bucket) is called by our server: it must not reach inside our network.
checkEndpointDnsfunctioncheckEndpoint (the written form) plus the DNS answer: a name that resolves to a private address is refused too, so a person's own bucket can never make our…
Clocktype
CLOUD_CONNECTORSconstantThe clouds a person may connect (ADR-005 amendment, krizaka-ts#235), as data: one line per provider, read by one flow.
cloudConnectorfunction
CloudConnectortype
CloudConnectorNametype
connectionAccessTokenfunctionThe accessToken an opendalStore asks for: renewed from the refresh token a minute before it expires, the rotated refresh token handed to onRotate (to r…
ConnectionTokenstype
ConnectorApptypeThe app's registration at the provider (from the admin's settings, the secret sealed).
ConnectorOverridetypeTests point a connector at a fake OAuth server; never built from what a person typed.
COUNTRY_OF_REGIONconstantThe country of each provider region (data: a new region is a line here, one table for every product, ADR-010).
countryOffunctionThe ISO country of a provider region, or undefined when the region is not mapped.
createProviderRegistryfunctionBuilds a registry from data (kinds) and the function that opens each implemented kind (adapters, by kind name): the table maps a kind to its adapter, nev…
createStoragefunctionThe storage of a product: an ObjectStore with the rules every upload obeys (a size limit passed in, a declared layout, bytes checked by magic number, a con…
defaultProviderRegistryconstantThe built-in kinds with their adapters.
defineLayoutfunction
describeStorefunctionThe descriptor of a configured store, from its configuration alone (see residencyOfConfig: a Bunny-shaped zone is dev only and never Canadian).
dropboxAuthorizeUrlfunctionThe address the person is sent to.
dropboxStorefunctionA person's Dropbox as an ObjectStore, App-folder access only.
DropboxStoreOptionstype
encryptedStorefunctionEncryption at rest over any ObjectStore (AES-256-GCM, a fresh IV per object, the object's key as authenticated data, so a file moved under another name doe…
exchangeDropboxCodefunctionExchanges the code Dropbox sent back (with the verifier kept at the start).
FetchLiketypeThe part of fetch the adapters use: inject the global one (fetch), or a fake in a test.
FieldSchematype
finishConnectionfunctionStep 2: the code the provider sent back, with the verifier of step 1.
FolderOptionstype
folderStorefunctionA folder on disk, for development, tests and the Mac apps.
hashedNamefunctionphoto.jpg and its bytes give photo.&lt;sha10>.jpg: a URL never changes content, so it is cached for good.
hashOffunctionThe first 10 hex characters of the SHA-256: the rule of the Krizaka media library (name.&lt;hash>.ext).
isPrivateAddressfunctionA loopback, private, link-local, unspecified or unique-local address, v4 or v6 (an IPv4-mapped v6 address is read as its v4).
keyIdOffunctionThe key_id a sealed value was wrapped under, or null for a v1. value (sealed by the single key).
KeyringtypeThe key port (ADR-005 amendment, krizaka-ts#239): all the vault asks of the key-encryption key is to wrap and unwrap a 32-byte data key.
LayouttypeThe prefixes a zone allows, as data: an unknown prefix is refused.
localKeyringfunctionThe in-process keyring: key is the current key-encryption key (named keyId), ring the older ones still needed to open values.
LocalKeystypeThe key-encryption keys held in this process: by key_id for v2. values, and the old single key (legacy) that still opens v1. values.
memoryObjectStorefunctionAn in-memory store, for unit tests and demos: the behaviour of the others (the contract kit runs on all).
NoticeKeytype
ObjectInfotype
ObjectStoretypeOne place files live.
openfunction
OpenContexttype
OpenedAdaptertype
OpenedProvidertype
openEnvelopefunctionOpens a v2. value with the key named in it, or a v1. value with the legacy key.
openProviderfunctiondefaultProviderRegistry.open (or the registry given): the factory of stores from a registered kind.
openWithfunctionopenEnvelope through a keyring.
parseEncryptionKeyfunctionThe 32-byte AES key of the encryption at rest, from its base64 form (the owner's key, passed in as a parameter).
parseVaultKeyfunctionThe 32-byte key of the vault, from its base64 form (the deployment's, passed in).
pkcePairfunctionA PKCE pair (RFC 7636): keep the verifier on the server with the state, send the challenge.
Presettype
presetEndpointfunctionThe endpoint a preset gives for a region, or null for the generic preset (typed by the person).
PROVIDER_KINDSconstant
ProviderAdaptertype
providerKindfunction
ProviderKindtype
ProviderKindNametypeThe kinds this package ships.
ProviderPlugintypeWhat a private package registers: the kind's data (the same shape as the built-in ones) and the function that opens its store.
ProviderRegistrytype
ProviderRuntimetypeserver = a deployed web BFF, dev = a developer's machine or CI, device = a mobile or macOS client.
ProviderScopetype
Provincetype
PROVINCE_OF_REGIONconstantThe province of a Canadian region.
provinceOffunction
refreshConnectionfunctionA fresh access token.
refreshDropboxTokenfunctionA fresh access token from the refresh token.
remoteStorefunctionAn HTTP object store with the shape of Bunny Storage: PUT/GET/DELETE on https://&lt;endpoint>/&lt;zone>/&lt;prefix>/&lt;key> with an AccessKey header, a direct…
RemoteStoreOptionstype
Residencytype
residencyLabelfunctionThe label of a configured store: Canada only when region and endpoint agree (residencyOfConfig); the province from the region table.
ResidencyLabeltypeWhere the files would live, as the screen says it before saving: key for the product's messages, text the English sentence.
residencyOffunctionCanada only when the table says so; an unmapped region is « elsewhere », never a guess in Canada's favour.
residencyOfConfigfunctionResidency of a configured store: the region AND the endpoint have to agree.
Resolvertype
revokeConnectionfunctionThe person's disconnect: revoked at the provider where it can be (already revoked is fine); the caller then deletes the sealed token.
revokeDropboxfunctionRevokes the connection at Dropbox (the person's disconnect).
rewrapEnvelopefunctionRe-wraps the data key under to (the rotation): the sealed secret is untouched.
rewrapWithfunctionrewrapEnvelope through a keyring: the data key is re-wrapped under ring.keyId; the sealed secret is untouched.
S3_PRESETSconstant
sealfunctionSeals a credential for the database (AES-256-GCM, a fresh IV, v1.&lt;iv|tag|ciphertext> in base64url).
sealEnvelopefunctionEnvelope encryption (ADR-005): a fresh data key seals the secret, and the key-encryption key (MEDIA_VAULT_KEY, named by keyId) wraps the data key.
sealWithfunctionsealEnvelope through a keyring (a KMS can wrap the data key): v2.&lt;ring.keyId>….
sniffTypefunctionThe content type the BYTES say they are, by magic number, never by the file name: png, jpeg, gif, webp, pdf, mp4, webm and svg.
startConnectionfunctionStep 1: where the person is sent.
Storagetype
STORAGE_CODEconstantThe refusals of this package: a stable code, and the HTTP status a route would answer.
StorageCodetype
StorageDepstype
StorageErrorclass
StorageLimitstype
storageNoticefunctionWhich sentence says where this object lives (« Not kept », « Kept: Krizaka (Canada, encrypted) »…), with its variables; the product's messages hold the words.
STORE_KINDconstantThe two kinds every product shares; the others are the product's.
StoreDescriptortypeWhere objects live, as the product says it: computed from the configuration, never typed.
StoredObjecttype
StoreOwnertypeWhose store it is: Krizaka's (the deployment's), the workspace's own, or the person's device.
SweepOptionstype
sweepOrphansfunctionDeletes the files under prefix that isReferenced does not know: the leftovers of an upload whose row was rolled back, of a purge interrupted between the…
SweepResulttype
testConnectionfunctionThe connection test of a store: writes a small probe object, reads it back and compares, then deletes it, the three things a kept file needs.
TestSteptype
trimSlashesfunctionTrims slashes at the ends without a regular expression (a long run of slashes must not be quadratic).
typeOfNamefunctionThe content type a file name suggests, for stores that do not keep one.
UploadInputtype
wherefunctionWhere an object lives, from the store kind recorded with it and the store configured now.
WhereOptionstype

@krizaka/storage/settings

NameKindWhat it is
limitsOffunction
readStorageSettingsfunctionThe module's settings from the product's accessor ((key) => service.get(key, ctx)); an unknown key falls back to the default.
STORAGE_DEFAULTSconstant
STORAGE_PROVIDERSconstant
StorageSecretstypeThe credentials the product reads from its environment and passes in.
storageSettingsconstantThe settings of « storage », a fragment the product adds to its registry (@krizaka/storage/settings): the admin picks the provider (local, http for a B…
StorageSettingstype
storeFromSettingsfunctionThe store the admin chose.
userConnectorsAllowedfunctionThe admin's policy on user-connected clouds, from the product's accessor.

@krizaka/storage/resolve

NameKindWhat it is
DEFAULT_MAX_SIGNED_SECONDSconstantA signed link never lives longer than this, whatever the admin or a caller asks (a kept file is personal data).
DeploymentStoragetypeWhat a deployment's environment says about one kind of kept data (documents, avatars, exports…), read by the product's edge and passed in: this module reads…
openResolvedfunctionThe store of a resolved configuration, encrypted when the environment holds a key, with the descriptor the product says.
ResolvedStoragetype
resolveStoragefunctionThe provider that applies to a kind of data, from the admin's settings over the deployment's environment.

@krizaka/storage/testkit

NameKindWhat it is
objectStoreContractfunctionWhat every ObjectStore adapter must do: the same behaviour behind the same port.
providerStoreContractfunctionWhat every ProviderStore must do.

@krizaka/storage/provider-admin

NameKindWhat it is
ActortypeWho acts: recorded in the audit trail.
AuditNotetypeWhat the service tells the audit trail (@krizaka/audit's record, wired by the product).
CatalogObjecttypeAn object the product holds, with the provider it lives at and the SHA-256 recorded at upload.
CreateProvidertype
createProviderServicefunction
dbProviderStorefunctionThe PostgreSQL store, on &lt;schema>.provider and &lt;schema>.default_provider (storageSchema), over the product's query.
LastTesttypeThe last connection test: steps and the residency it found, never a value.
memoryProviderStorefunctionThe in-memory store, for unit tests.
moveBatchfunctionOne bounded batch of the move (ADR-005 §3), run from a leased outbox worker, resumable: get from the old store, check the hash recorded at upload, put to the…
MoveResulttype
ObjectCatalogtypeThe product's side of a move: it owns the objects table.
ProviderRecordtypeA provider as stored.
ProviderServicetype
ProviderServiceDepstype
ProviderStatustype
ProviderStoretypeWhere the providers live.
PublicProvidertypeA provider as the API returns it: every secret is set or not set, nothing else, ever.
readDualfunctionReads from the provider the row names, and falls back to the older providers while a move is running.
sha256Hexfunction
storageSchemaconstantThe tables of the admin's providers, in their own Postgres schema (ADR-001 §3).

@krizaka/storage/opendal

NameKindWhat it is
OPENDAL_SERVICESconstantThe OpenDAL services this package lets a provider open (ADR-005 amendment, krizaka-ts#235), as data.
OpendalObjectStoretypeThe port, plus a streaming read for large files (the port reads whole files): OpenDAL's reader as a Node stream.
OpendalServicetype
opendalStorefunctionOne adapter over Apache OpenDAL for the providers that have no adapter of their own (Google Drive, OneDrive, Azure Blob, GCS, Backblaze B2, Swift, WebDAV/Nex…
OpendalStoreOptionstype
storageErrorOffunctionOpenDAL's errors carry their kind as the first word of the message (NotFound (permanent) at read…): mapped onto this package's codes.

Package

Version on npm0.8.1 (npm)
LicenceApache-2.0
Node.js>=22
Dependencies@krizaka/db workspace:^, @krizaka/settings workspace:^, arctic 3.7.0
Peer dependenciesvitest >=4, zod >=3.23, opendal >=0.49.13 <0.50
Sourcestorage

Changelog

0.8.1

Patch changes

  • The READMEs, comments and published declarations no longer name the products that left the organisation (krizaka/.github#413, rule no-departed-published). The sources were cleaned in #262 with an empty changeset, so the published tarballs still carried the old text; this release replaces them. No behaviour change.
  • Updated dependencies [2272b57]

0.8.0

Minor changes

  • Every classic cloud through libraries (ADR-005 amendment, #235): opendalStore (@krizaka/storage/opendal, Apache OpenDAL as an optional peer) behind the port, with an allow-list of services, the endpoint and DNS checks kept, error mapping onto STORAGE_* and readStream; CLOUD_CONNECTORS and the Arctic-based connection flow (authorization code + PKCE S256, refresh with rotation, revocation) for Dropbox, Google Drive and OneDrive.
  • The registry opens the OpenDAL kinds and takes private kinds (ADR-005 amendment, #239): google-drive and onedrive implemented at user scope, azblob, gcs, b2 and webdav at admin scope, each kind mapped to its adapter through a table; createProviderRegistry({ kinds, adapters }).with({ kind, open }) registers a private provider without touching the core (every rule applies: scopes, runtimes, devOnly, url fields checked, secrets sealed); Keyring becomes a port ({ keyId, wrap, unwrap }, localKeyring the default, sealWith/openWith/rewrapWith), ready for a KMS with the stored v2.<key_id> format unchanged. Breaking for 0.x consumers: openProvider is asynchronous, and the old Keyring type ({ keys, legacy }) is renamed LocalKeys.

Patch changes

0.7.0

Minor changes

  • storageSettings has storage.userConnectors (flag, off by default): the admin's policy on whether people may connect their own cloud (Dropbox, an S3 of their own). userConnectorsAllowed(read) reads it; anything but an explicit true is off.

Patch changes

0.6.0

Minor changes

  • Dropbox as a user-scope provider (ADR-005, slice 4): dropboxStore (App folder, upload sessions above 150 MB, no signed or public link: the app streams), the OAuth 2 PKCE helpers (pkcePair, dropboxAuthorizeUrl, exchangeDropboxCode, refreshDropboxToken, revokeDropbox), the new code STORAGE_DISCONNECTED for a revoked or expired connection, and dropbox opened by openProvider.
  • Providers in the database (ADR-005, slice 2), in @krizaka/storage/provider-admin: storageSchema, dbProviderStore / memoryProviderStore with a contract, createProviderService (create, test, set default, change key, disable, retire, rotate the vault key, bootstrap from the environment; secrets returned as set / not set, every change audited), moveBatch (SHA-256-verified move) and readDual (reads from both stores during a move).

0.5.0

Minor changes

  • Provider registry (ADR-005, slice 1): PROVIDER_KINDS as data, openProvider with the runtime and environment guards (devOnly refused in prod, folder only in dev), PROVINCE_OF_REGION and residencyLabel, testConnection and checkEndpointDns moved from Yomizaka, and the envelope vault (sealEnvelope, openEnvelope, rewrapEnvelope, keyIdOf, v2. values with a key_id; v1. still opens).

0.4.0

Minor changes

  • sweepOrphans(store, prefix, isReferenced, { dryRun, limit }): deletes the files under a prefix that nothing references (leftovers of a rolled-back upload or an interrupted purge), reports scanned / orphans / deleted / failed, never stops on one failing delete.

0.3.1

Patch changes

  • The default entry is safe on Edge and in the browser: no node: import, nothing reads a file. It carries the types and the port, the region table, describeStore, storageNotice, the endpoint check, sniffType, the key rules and the in-memory store. The adapters that hash, sign, encrypt or touch the disk (createStorage, bucketStore, remoteStore, folderStore, encryptedStore, hashedName, seal/open) are in @krizaka/storage/node, and . still resolves to them under the node condition, so Node code keeps working unchanged. A test bundles the default entry for the browser and fails on any node: or fs import.

0.3.0

Minor changes

  • The four pieces Yomizaka kept locally move in (#146), and the admin-over-environment rule is decided. encryptedStore(inner, key) (AES-256-GCM over any ObjectStore, key as a parameter, ciphertext only at the provider, signedUrl/publicUrl null) with parseEncryptionKey; the vault seal/open/parseVaultKey for credentials in the database; the shared region table COUNTRY_OF_REGION with countryOf, residencyOf and residencyOfConfig (Canada only when the region AND the endpoint agree, never for a Bunny-shaped zone or an unmapped region); where(kind, active, { fixed, devOnly }), describeStore and storageNotice (the generated residency sentence, from the configuration). New subpath @krizaka/storage/resolve: resolveStorage(env, admin) applies the admin's storage.* settings over the deployment's environment for any kind of data (documents, avatars), caps the signed link, and openResolved builds the store (encrypted when a key is given). When the environment says none and the admin turns keeping on, resolveStorage throws StorageError STORAGE_KEEPING_OFF (new codes STORAGE_KEEPING_OFF, STORAGE_UNREADABLE, STORAGE_BAD_SECRET_KEY): keeping is only turned on from the environment.

0.2.0

Minor changes

  • New packages (#44). @krizaka/storage: the ObjectStore port (put, get, head, delete, list, signedUrl, publicUrl), remoteStore (HTTP zone, token-signed links), bucketStore (any S3-compatible service, Signature V4 checked against AWS's vectors), folderStore, memoryObjectStore, createStorage (size limit passed in, declared layout, magic-number check, content-hashed names), checkEndpoint, @krizaka/storage/settings (storage.provider local / http / s3, limits and coordinates; storeFromSettings) and objectStoreContract. @krizaka/video: the VideoProvider port, streamVideo (Bunny Stream: create, delete, server-signed TUS sessions, embed and mobile view tokens, signed HLS and poster links, signed webhooks), the signing helpers and videoProviderContract.

On this page