@push.rocks/smartregistry 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartregistry',
6
- version: '2.4.0',
6
+ version: '2.5.0',
7
7
  description: 'A composable TypeScript library implementing OCI, NPM, Maven, Cargo, Composer, PyPI, and RubyGems registries for building unified container and package registries'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSwyQkFBMkI7SUFDakMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLG9LQUFvSztDQUNsTCxDQUFBIn0=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartregistry",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "private": false,
5
5
  "description": "A composable TypeScript library implementing OCI, NPM, Maven, Cargo, Composer, PyPI, and RubyGems registries for building unified container and package registries",
6
6
  "main": "dist_ts/index.js",
package/readme.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## Issue Reporting and Security
6
6
 
7
- For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who want to sign a contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
7
+ For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
8
8
 
9
9
  ## ✨ Features
10
10
 
@@ -82,6 +82,19 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
82
82
  - ✅ Dependency resolution
83
83
  - ✅ Legacy API compatibility
84
84
 
85
+ ### 🌐 Upstream Proxy & Caching
86
+ - **Multi-Upstream Support**: Configure multiple upstream registries per protocol with priority ordering
87
+ - **Scope-Based Routing**: Route specific packages/scopes to different upstreams (e.g., `@company/*` → private registry)
88
+ - **S3-Backed Cache**: Persistent caching using existing S3 storage with URL-based cache paths
89
+ - **Circuit Breaker**: Automatic failover with configurable thresholds
90
+ - **Stale-While-Revalidate**: Serve cached content while refreshing in background
91
+ - **Content-Aware TTLs**: Different TTLs for immutable (tarballs) vs mutable (metadata) content
92
+
93
+ ### 🔌 Enterprise Extensibility
94
+ - **Pluggable Auth Provider** (`IAuthProvider`): Integrate LDAP, OAuth, SSO, or custom auth systems
95
+ - **Storage Event Hooks** (`IStorageHooks`): Quota tracking, audit logging, virus scanning, cache invalidation
96
+ - **Request Actor Context**: Pass user/org info through requests for audit trails and rate limiting
97
+
85
98
  ## 📥 Installation
86
99
 
87
100
  ```bash
@@ -648,6 +661,217 @@ const canWrite = await authManager.authorize(
648
661
  );
649
662
  ```
650
663
 
664
+ ### 🌐 Upstream Proxy Configuration
665
+
666
+ ```typescript
667
+ import { SmartRegistry, IRegistryConfig } from '@push.rocks/smartregistry';
668
+
669
+ const config: IRegistryConfig = {
670
+ storage: { /* S3 config */ },
671
+ auth: { /* Auth config */ },
672
+ npm: {
673
+ enabled: true,
674
+ basePath: '/npm',
675
+ upstream: {
676
+ enabled: true,
677
+ upstreams: [
678
+ {
679
+ id: 'company-private',
680
+ name: 'Company Private NPM',
681
+ url: 'https://npm.internal.company.com',
682
+ priority: 1, // Lower = higher priority
683
+ enabled: true,
684
+ scopeRules: [
685
+ { pattern: '@company/*', action: 'include' },
686
+ { pattern: '@internal/*', action: 'include' },
687
+ ],
688
+ auth: { type: 'bearer', token: process.env.NPM_PRIVATE_TOKEN },
689
+ },
690
+ {
691
+ id: 'npmjs',
692
+ name: 'NPM Public Registry',
693
+ url: 'https://registry.npmjs.org',
694
+ priority: 10,
695
+ enabled: true,
696
+ scopeRules: [
697
+ { pattern: '@company/*', action: 'exclude' },
698
+ { pattern: '@internal/*', action: 'exclude' },
699
+ ],
700
+ auth: { type: 'none' },
701
+ cache: { defaultTtlSeconds: 300 },
702
+ resilience: { timeoutMs: 30000, maxRetries: 3 },
703
+ },
704
+ ],
705
+ cache: { enabled: true, staleWhileRevalidate: true },
706
+ },
707
+ },
708
+ oci: {
709
+ enabled: true,
710
+ basePath: '/oci',
711
+ upstream: {
712
+ enabled: true,
713
+ upstreams: [
714
+ {
715
+ id: 'dockerhub',
716
+ name: 'Docker Hub',
717
+ url: 'https://registry-1.docker.io',
718
+ priority: 1,
719
+ enabled: true,
720
+ auth: { type: 'none' },
721
+ },
722
+ {
723
+ id: 'ghcr',
724
+ name: 'GitHub Container Registry',
725
+ url: 'https://ghcr.io',
726
+ priority: 2,
727
+ enabled: true,
728
+ scopeRules: [{ pattern: 'ghcr.io/*', action: 'include' }],
729
+ auth: { type: 'bearer', token: process.env.GHCR_TOKEN },
730
+ },
731
+ ],
732
+ },
733
+ },
734
+ };
735
+
736
+ const registry = new SmartRegistry(config);
737
+ await registry.init();
738
+
739
+ // Requests for @company/* packages go to private registry
740
+ // Other packages proxy through to npmjs.org with caching
741
+ ```
742
+
743
+ ### 🔌 Custom Auth Provider
744
+
745
+ ```typescript
746
+ import { SmartRegistry, IAuthProvider, IAuthToken, ICredentials, TRegistryProtocol } from '@push.rocks/smartregistry';
747
+
748
+ // Implement custom auth (e.g., LDAP, OAuth)
749
+ class LdapAuthProvider implements IAuthProvider {
750
+ constructor(private ldapClient: LdapClient) {}
751
+
752
+ async authenticate(credentials: ICredentials): Promise<string | null> {
753
+ const result = await this.ldapClient.bind(credentials.username, credentials.password);
754
+ return result.success ? credentials.username : null;
755
+ }
756
+
757
+ async validateToken(token: string, protocol?: TRegistryProtocol): Promise<IAuthToken | null> {
758
+ const session = await this.sessionStore.get(token);
759
+ if (!session) return null;
760
+ return {
761
+ userId: session.userId,
762
+ scopes: session.scopes,
763
+ readonly: session.readonly,
764
+ created: session.created,
765
+ };
766
+ }
767
+
768
+ async createToken(userId: string, protocol: TRegistryProtocol, options?: ITokenOptions): Promise<string> {
769
+ const token = crypto.randomUUID();
770
+ await this.sessionStore.set(token, { userId, protocol, ...options });
771
+ return token;
772
+ }
773
+
774
+ async revokeToken(token: string): Promise<void> {
775
+ await this.sessionStore.delete(token);
776
+ }
777
+
778
+ async authorize(token: IAuthToken | null, resource: string, action: string): Promise<boolean> {
779
+ if (!token) return action === 'read'; // Anonymous read-only
780
+ // Check LDAP groups, roles, etc.
781
+ return this.checkPermissions(token.userId, resource, action);
782
+ }
783
+ }
784
+
785
+ // Use custom provider
786
+ const registry = new SmartRegistry({
787
+ ...config,
788
+ authProvider: new LdapAuthProvider(ldapClient),
789
+ });
790
+ ```
791
+
792
+ ### 📊 Storage Hooks (Quota & Audit)
793
+
794
+ ```typescript
795
+ import { SmartRegistry, IStorageHooks, IStorageHookContext } from '@push.rocks/smartregistry';
796
+
797
+ const storageHooks: IStorageHooks = {
798
+ // Block uploads that exceed quota
799
+ async beforePut(ctx: IStorageHookContext) {
800
+ if (ctx.actor?.orgId) {
801
+ const usage = await getStorageUsage(ctx.actor.orgId);
802
+ const quota = await getQuota(ctx.actor.orgId);
803
+
804
+ if (usage + (ctx.metadata?.size || 0) > quota) {
805
+ return { allowed: false, reason: 'Storage quota exceeded' };
806
+ }
807
+ }
808
+ return { allowed: true };
809
+ },
810
+
811
+ // Update usage tracking after successful upload
812
+ async afterPut(ctx: IStorageHookContext) {
813
+ if (ctx.actor?.orgId && ctx.metadata?.size) {
814
+ await incrementUsage(ctx.actor.orgId, ctx.metadata.size);
815
+ }
816
+
817
+ // Audit log
818
+ await auditLog.write({
819
+ action: 'storage.put',
820
+ key: ctx.key,
821
+ protocol: ctx.protocol,
822
+ actor: ctx.actor,
823
+ timestamp: ctx.timestamp,
824
+ });
825
+ },
826
+
827
+ // Prevent deletion of protected packages
828
+ async beforeDelete(ctx: IStorageHookContext) {
829
+ if (await isProtectedPackage(ctx.key)) {
830
+ return { allowed: false, reason: 'Cannot delete protected package' };
831
+ }
832
+ return { allowed: true };
833
+ },
834
+
835
+ // Log all access for compliance
836
+ async afterGet(ctx: IStorageHookContext) {
837
+ await accessLog.write({
838
+ action: 'storage.get',
839
+ key: ctx.key,
840
+ actor: ctx.actor,
841
+ timestamp: ctx.timestamp,
842
+ });
843
+ },
844
+ };
845
+
846
+ const registry = new SmartRegistry({
847
+ ...config,
848
+ storageHooks,
849
+ });
850
+ ```
851
+
852
+ ### 👤 Request Actor Context
853
+
854
+ ```typescript
855
+ // Pass actor information through requests for audit/quota tracking
856
+ const response = await registry.handleRequest({
857
+ method: 'PUT',
858
+ path: '/npm/my-package',
859
+ headers: { 'Authorization': 'Bearer <token>' },
860
+ query: {},
861
+ body: packageData,
862
+ actor: {
863
+ userId: 'user123',
864
+ tokenId: 'token-abc',
865
+ ip: req.ip,
866
+ userAgent: req.headers['user-agent'],
867
+ orgId: 'org-456',
868
+ sessionId: 'session-xyz',
869
+ },
870
+ });
871
+
872
+ // Actor info is available in storage hooks for quota/audit
873
+ ```
874
+
651
875
  ## ⚙️ Configuration
652
876
 
653
877
  ### Storage Configuration
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartregistry',
6
- version: '2.4.0',
6
+ version: '2.5.0',
7
7
  description: 'A composable TypeScript library implementing OCI, NPM, Maven, Cargo, Composer, PyPI, and RubyGems registries for building unified container and package registries'
8
8
  }