Skip to content

Random Port Generator — Ruby source

Generate one or many random TCP/UDP port numbers across registered, ephemeral, or the full range - optionally unique. Runs entirely in your browser with crypto-grade randomness.

This is the Ruby implementation — the same logic the interactive tool runs, in a shareable, citable form.

# random-port-generator — Ruby port: random/dynamic port picker.
#
# Display port of the CosmoDev Random Port Generator tool — same contract as
# cli/random-port-generator/random-port-generator.go (the live Go twin) and
# src/lib/random-port.ts (the canonical TypeScript implementation). Pure +
# deterministic via an injectable rng (the TS test surface); invalid custom
# ranges raise ArgumentError, mirroring the TS throw. The default rng is
# SecureRandom.random_number — a CSPRNG standing in for the TS
# crypto.getRandomValues default.
require 'securerandom'

module RandomPort
  RANGES = {
    'any' => [1, 65_535],
    'registered' => [1024, 49_151], # the TS default (registered ports)
    'ephemeral' => [49_152, 65_535] # dynamic/client ports
  }.freeze

  DEFAULT_RNG = -> { SecureRandom.random_number } # CSPRNG float in [0, 1)

  module_function

  # resolveRange() in the TS lib.
  def resolve_range(range: 'registered', min: nil, max: nil)
    return [(min || 1), (max || 65_535)] if range == 'custom'
    RANGES.fetch(range)
  end

  # assertRange() in the TS lib — the TS Error becomes an exception.
  def assert_range(lo, hi)
    unless lo.is_a?(Integer) && hi.is_a?(Integer) && lo >= 0 && lo <= 65_535 && hi <= 65_535 && lo <= hi
      raise ArgumentError, "Invalid port range #{lo}-#{hi}"
    end
  end

  # randomPort() in the TS lib — Port() in the Go twin.
  def random_port(range: 'registered', min: nil, max: nil, rng: DEFAULT_RNG)
    lo, hi = resolve_range(range: range, min: min, max: max)
    assert_range(lo, hi)
    lo + (rng.call * (hi - lo + 1)).floor
  end

  # randomPorts() in the TS lib — Ports() in the Go twin.
  def random_ports(count: 1, unique: false, range: 'registered', min: nil, max: nil, rng: DEFAULT_RNG)
    count = [1, count].max
    lo, hi = resolve_range(range: range, min: min, max: max)
    assert_range(lo, hi)
    unless unique
      return Array.new(count) { random_port(range: range, min: min, max: max, rng: rng) }
    end

    # Fisher-Yates partial shuffle over the range to pick n unique ports.
    capacity = hi - lo + 1
    n = [count, capacity].min
    pool = (lo..hi).to_a
    n.times do |i|
      j = i + (rng.call * (capacity - i)).floor
      pool[i], pool[j] = pool[j], pool[i]
    end
    pool.first(n)
  end
end

# Vectors from src/lib/random-port.test.ts — a fixed rng pins every draw.
zero = -> { 0.0 }
half = -> { 0.5 }
puts "registered+0 = #{RandomPort.random_port(rng: zero)}" # 1024
puts "any+0 = #{RandomPort.random_port(range: 'any', rng: zero)}" # 1
puts "ephemeral+0 = #{RandomPort.random_port(range: 'ephemeral', rng: zero)}" # 49152
puts "custom 8000-8000 @0.9 = #{RandomPort.random_port(range: 'custom', min: 8000, max: 8000, rng: -> { 0.9 })}" # 8000
puts "resolveRange(custom defaults) = #{RandomPort.resolve_range(range: 'custom').join('..')}" # 1..65535
puts "count=5 @0 = #{RandomPort.random_ports(count: 5, rng: zero).inspect}" # [1024 x5]
puts "unique 3 in 1..10 @0.5 = #{RandomPort.random_ports(count: 3, unique: true, range: 'custom', min: 1, max: 10, rng: half).inspect}" # [6, 1, 7]
puts "unique 5 in 1..3 @0.5 = #{RandomPort.random_ports(count: 5, unique: true, range: 'custom', min: 1, max: 3, rng: half).inspect}" # capped at capacity 3
p = RandomPort.random_port # default CSPRNG, registered range
puts "default rng registered = #{p} (in 1024..49151)"
[
  [{ range: 'custom', min: 70_000 }, 'min above 65535'],
  [{ range: 'custom', min: 100, max: 50 }, 'min above max']
].each do |opts, why|
  RandomPort.random_port(**opts)
rescue ArgumentError => e
  puts "invalid: #{e.message} (#{why})"
end

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →