roster-server 2.1.8 → 2.1.12

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.
Files changed (2) hide show
  1. package/index.js +46 -23
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -5,6 +5,7 @@ const https = require('https');
5
5
  const tls = require('tls');
6
6
  const { EventEmitter } = require('events');
7
7
  const Greenlock = require('./vendor/greenlock-express/greenlock-express.js');
8
+ const GreenlockShim = require('./vendor/greenlock-express/greenlock-shim.js');
8
9
  const log = require('lemonlog')('roster');
9
10
 
10
11
  // CRC32 implementation for deterministic port assignment
@@ -707,7 +708,7 @@ class Roster {
707
708
  return this.startLocalMode();
708
709
  }
709
710
 
710
- const greenlock = Greenlock.init({
711
+ const greenlockOptions = {
711
712
  packageRoot: __dirname,
712
713
  configDir: this.greenlockStorePath,
713
714
  maintainerEmail: this.email,
@@ -724,9 +725,16 @@ class Roster {
724
725
  else if (event === 'warning') log.warn(msg);
725
726
  else log.info(msg);
726
727
  }
728
+ };
729
+ // Keep a direct greenlock runtime handle so we can call get() explicitly under Bun
730
+ // before binding :443, avoiding invalid non-TLS responses on startup.
731
+ const greenlockRuntime = GreenlockShim.create(greenlockOptions);
732
+ const greenlock = Greenlock.init({
733
+ ...greenlockOptions,
734
+ greenlock: greenlockRuntime
727
735
  });
728
736
 
729
- return greenlock.ready(glx => {
737
+ return greenlock.ready(async glx => {
730
738
  const httpServer = glx.httpServer();
731
739
 
732
740
  // Group sites by port
@@ -845,6 +853,26 @@ class Roster {
845
853
  }
846
854
  return null;
847
855
  };
856
+ const ensureBunDefaultPems = async (primaryDomain) => {
857
+ let pems = resolvePemsForServername(primaryDomain);
858
+ if (pems) return pems;
859
+
860
+ const certSubject = primaryDomain.startsWith('*.') ? wildcardRoot(primaryDomain) : primaryDomain;
861
+ log.warn(`⚠️ Bun runtime detected and cert files missing for ${primaryDomain}; requesting certificate via Greenlock before HTTPS bind`);
862
+ try {
863
+ await greenlockRuntime.get({ servername: certSubject });
864
+ } catch (error) {
865
+ log.error(`❌ Failed to obtain certificate for ${certSubject} under Bun:`, error?.message || error);
866
+ }
867
+
868
+ pems = resolvePemsForServername(primaryDomain);
869
+ if (pems) return pems;
870
+
871
+ throw new Error(
872
+ `Bun runtime could not load TLS certificate files for ${primaryDomain}. ` +
873
+ `Refusing to start HTTPS on port ${portNum} to avoid serving invalid TLS.`
874
+ );
875
+ };
848
876
 
849
877
  if (portNum === this.defaultPort) {
850
878
  // Bun has known gaps around SNICallback compatibility.
@@ -855,28 +883,23 @@ class Roster {
855
883
 
856
884
  if (isBunRuntime) {
857
885
  const primaryDomain = Object.keys(portData.virtualServers)[0];
858
- // Greenlock stores certs by subject (e.g. tagnu.com), not by wildcard (*.tagnu.com)
859
- const defaultPems = resolvePemsForServername(primaryDomain);
860
-
861
- if (defaultPems) {
862
- httpsServer = https.createServer({
863
- ...tlsOpts,
864
- key: defaultPems.key,
865
- cert: defaultPems.cert,
866
- SNICallback: (servername, callback) => {
867
- try {
868
- const pems = resolvePemsForServername(servername) || defaultPems;
869
- callback(null, tls.createSecureContext({ key: pems.key, cert: pems.cert }));
870
- } catch (error) {
871
- callback(error);
872
- }
886
+ // Under Bun, avoid glx.httpsServer fallback (may serve invalid TLS on :443).
887
+ // Require concrete PEM files and create native https server directly.
888
+ const defaultPems = await ensureBunDefaultPems(primaryDomain);
889
+ httpsServer = https.createServer({
890
+ ...tlsOpts,
891
+ key: defaultPems.key,
892
+ cert: defaultPems.cert,
893
+ SNICallback: (servername, callback) => {
894
+ try {
895
+ const pems = resolvePemsForServername(servername) || defaultPems;
896
+ callback(null, tls.createSecureContext({ key: pems.key, cert: pems.cert }));
897
+ } catch (error) {
898
+ callback(error);
873
899
  }
874
- }, dispatcher);
875
- log.warn(`⚠️ Bun runtime detected: using file-based TLS with SNI for ${primaryDomain} on port ${portNum}`);
876
- } else {
877
- log.warn(`⚠️ Bun runtime detected but cert files missing for ${primaryDomain}; falling back to Greenlock HTTPS server`);
878
- httpsServer = glx.httpsServer(tlsOpts, dispatcher);
879
- }
900
+ }
901
+ }, dispatcher);
902
+ log.warn(`⚠️ Bun runtime detected: using file-based TLS with SNI for ${primaryDomain} on port ${portNum}`);
880
903
  } else {
881
904
  httpsServer = glx.httpsServer(tlsOpts, dispatcher);
882
905
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roster-server",
3
- "version": "2.1.8",
3
+ "version": "2.1.12",
4
4
  "description": "👾 RosterServer - A domain host router to host multiple HTTPS.",
5
5
  "main": "index.js",
6
6
  "scripts": {