npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

thesidedoor-core

v0.5.2

Published

Self-hosted AI, access, storage, jobs, notifications, metrics and operations infrastructure

Readme

Sidedoor core

Server modules for applications that use user-configured AI providers and self-hosted infrastructure. Requires Node 22 or later. React is provided separately by thesidedoor.

This package is Sidedoor's shared backend. Follow the repository verification guide to validate the current source. Public package availability depends on the latest completed release.

AI

import { ProviderRegistry } from 'thesidedoor-core/ai';
import { apiProviders } from 'thesidedoor-core/ai/providers';

const registry = new ProviderRegistry({
  providers: apiProviders(),
  credentials: {
    async resolve(provider) {
      if (provider !== 'openai') throw new Error('Provider is not configured');
      return { apiKey: configuredKey };
    },
  },
});

for await (const event of registry.generate({
  provider: 'openai',
  model: configuredModel,
  messages: [{ role: 'user', content: [{ type: 'text', text: prompt }] }],
  signal: requestSignal,
})) {
  if (event.type === 'text') writeText(event.text);
}

configuredKey, configuredModel, prompt, requestSignal and writeText are application inputs. Resolve credentials only after authorizing the request. Keep credentials server-side. The runnable example includes the process lifecycle.

For a new provider, implement ProviderAdapter and register it with ProviderRegistry. Declare actual capabilities, implement readiness and model discovery, and emit the common generation events. Add catalog metadata when the provider needs to appear in shared credential forms. Test the transport against an HTTP or process boundary, including cancellation and errors.

Modules

| Import | Responsibility | | ----------------------------------------------------------------------- | ------------------------------------------------------------ | | /ai, /ai/providers, /providers/catalog | Generation, adapters, capabilities and credential metadata | | /access, /access/http, /access/browser | Accounts, sessions, passkeys, recovery and HTTP protocol | | /configuration | Encrypted instance configuration | | /configuration/owned-credentials, /configuration/credential-sharing | Revisioned owner credentials and explicit sharing | | /storage, /storage/sql | File/SQL persistence and transaction adapters | | /runtime/process, /runtime/cli, /runtime/ssh | Local and remote execution | | /runtime/outbox, /runtime/task-loop | Durable work reconciliation and worker loops | | /runtime/semaphore | Redis capacity leases with exact token ownership | | /observability, /observability/store, /ai/usage | Explicit metric collection, persistence and token accounting | | /setup, /notifications | Setup state and notification delivery primitives |

See the architecture guide before integrating persistence, credentials or access. The caller owns authorization and the Serializable transaction that applies related state changes. Shared persistence methods do not grant authority by themselves.

retrySerializableTransaction retries recognized serialization conflicts and deadlocks with ten fresh transactions and exponential equal jitter. Total backoff is bounded at 2,130 ms, excluding transaction execution. Cancellation interrupts the wait; exhaustion preserves the final database error. Keep external writes outside the callback so retrying publication cannot upload or send again.

runJobExecution() from /runtime/outbox owns durable execution admission, optional temporary workspaces, and cleanup receipts. Supply a fresh Serializable transaction adapter, the executor instance UUID, authority validation, and an existing trusted workspace root with its location UUID. Work starts only after the workspace identity is recorded. Lost commit responses are reconciled against the exact execution without replaying the work. Await all owned I/O or call markCleanupUnconfirmed() when its outcome remains uncertain. Uncertainty raises JobExecutionCleanupError, retains workspace evidence, and blocks settlement. Already committed application results remain committed.

Workspace recovery requires independent proof that the exact executor and its I/O have stopped. Read the current journal record before choosing recovery for an unattached creation intent or removal using its recorded directory identity. A missing directory, an expired queue lease, or a worker failure does not supply termination proof. openExecutionLocation(root, { create: true }) initializes a new private location with an inode-bound marker; later calls verify that identity. Existing roots with missing or invalid markers require operator repair. Opening without create never recreates a missing location. Operator termination verification remains an integration requirement; the filesystem helpers do not provide it.

copyOwnedReadableToFile() from /storage copies an acquired Node stream to an exclusive destination and waits for source and destination closure. Supply the actual transport stream through openSource; late acquisitions retain cleanup continuations after cancellation. Failed copies leave partial files in the caller's workspace. Node stream errors can combine read and destruction failures, so ambiguous errors raise StorageReadCleanupError and keep execution cleanup unresolved. A transport with stronger release evidence can implement OwnedByteReader and use copyOwnedBytesToFile() directly. Stream diagnostics retain at most eight errors plus a count.

RedisSemaphoreLease supplies advisory capacity limits through a caller-owned Redis eval connection. Commands must execute in order without replay after release. Acquisition retries reuse the same token, and release removes only that token. Each token belongs to one logical execution and one handle; never share it or reuse it for later work. After connection loss, confirm the original session is drained before issuing recovery commands on another connection. waitForSemaphore owns cancellation during acquisition and waiting; unconfirmed release raises SemaphoreCleanupError, which retains the parent execution guard. Lease expiry allows advisory capacity reuse. It does not prove provider completion; durable provider outcomes still need independent execution and settlement evidence.

runStorageProbe() raises StorageProbeCleanupError when upload or cleanup ownership remains unresolved. The error retains the cleanup job ID and original causes. Shared job execution recognizes it and preserves the owning workspace. After a lost settlement response, the probe reads its exact execution receipt before reporting uncertainty. Fully confirmed cleanup followed by revoked authority still reports the authority failure.

Owned byte copies, Node readable copies and local reader copies return { sha256, bytes } after all bytes are written and handles are confirmed closed. The digest covers detached source chunks. It proves copied content, not fsync durability. Cancellation during closure rejects the copy instead of returning proof.

For verified migrations, supply writeReferenceSet.verifyArtifact. The captured callback runs after each confirmed upload and before publication, outside database retries. It receives the immutable operation identity, reference, target and backend descriptor as detached values. Independently read back the destination and compare its content evidence with the source copy. A failed verification retains the upload as unreferenced, leaves later uploads unstarted and preserves cleanup errors.

writeReferenceSet owns every distinct input stream from entry and confirms their closure concurrently before returning. A close failure after publication leaves the committed references intact and reports cleanup uncertainty to the execution lifecycle. Streams must emit close after destruction; emitClose: false cannot provide that confirmation and times out conservatively.

StorageRelocationRegistry preserves explicit file-migration provenance without changing sealed job results. The copy orchestrator must hash source bytes, upload to an immutable destination, and independently read back and hash the destination. Record matching hashes and byte counts in the same Serializable transaction as reference replacement and application URL updates. The registry checks exact consumer transfers, retirement evidence and preserved ownership scopes. Supplied readback evidence is trusted application testimony; the registry does not perform the copy itself.

resolve() follows only recorded relocation edges for the requested consumer. It rejects ordinary regeneration gaps and erased scopes. Supply an explicit maxHops and an optional cancellation signal; exceeding the limit rejects proof. An unchanged asset returns content: null. listForSubject() discovers retained receipts in pages of 100, including after tombstoning or asset metadata removal. eraseForSubject() requires the exact preparing cleanup job, tombstone, generation, epoch and snapshot retention policy. In the caller's Serializable transaction it retains both endpoint dependencies in immutable cleanup manifests, then removes a bounded page of receipts and their scope indexes. Missing asset attribution stays explicitly unresolved. The cleanup resolver must handle these dependencies before freezing discovery. An opaque allocation marker prevents erased operation reuse. Historical aliases and source objects must remain available while jobs depend on them.

StorageCleanupAttribution.inspectPage() classifies persisted manifest entries under the exact preparing job and tombstone. It validates canonical asset, retirement and relocation attribution against the historical backend, including after asset metadata removal. Missing attribution, unsupported raw entries and registered consumers remain unresolved. Its proposed targets do not authorize deletion; collector coverage, write drainage and execution ownership still apply.

/providers/transport supplies createProviderTransport() for authenticated HTTP. Capture explicit destination and method rules, then provide an admit callback that rechecks the request or job authority and the selected credential revision. Inject authenticatedFetch into the provider or SDK so each HTTP attempt passes admission, including retries and polling. Generation destinations need their own rules; a credential-validation endpoint does not authorize generation URLs. Keep unauthenticated output downloads separate. The transport rejects redirects, combines cancellation signals and bounds failed-body cleanup to one second. Admission callbacks must check their signal before committing side effects.

Use OwnedCredentials.resolve() for execution. An authorized settings editor can use readForEdit(target, expectedRevision) to repair a disabled credential. That read requires the exact displayed revision and leaves availability unchanged. Keep its values server-side and publish edits through the validated replacement flow.

createMediaTransport() from /providers/transport downloads reference audio and generated media as bounded bytes. Supply maxBytes, timeoutMs, and an admit callback that checks both destination policy and the original execution authority on every redirect hop. Call downloadMedia(url, { signal }); it sends GET requests without credentials or caller-supplied headers, preserves signed query strings, and follows at most 20 redirects. Media admission should validate saved credential metadata without recording provider usage. The downloader bounds actual streamed bytes, includes body reads in its deadline, and cancels discarded bodies. Provider credentials belong only on the separate authenticated transport.

/configuration/credential-client provides browser-safe edit schemas, immutable drafts and lost-response reconciliation. Prepare from the displayed owner, scope and revision. Confirming an unverified save reuses that draft. Reconciliation checks a fresh authenticated snapshot and never resubmits a mutation. A confirmed write can now be disabled, so inspect the returned key's availability. Applications supply HTTP transport and authorization.

ProcessRunner.streamBytes() returns owned Uint8Array chunks from stdout and stderr through the same process lifecycle as text execution. Use it for decoded audio and other binary output. maxBufferedBytes controls the queue high-water mark; pipe chunks and Node stream buffers add bounded overhead. Slow consumers pause both pipes. Set maxOutputBytes: null explicitly when cumulative output must be unlimited, and consume chunks without retaining the full result. Text execution keeps its existing buffering behavior and output limits. Cancellation terminates the process group independently of consumer progress and reports unconfirmed cleanup.