nomad-cli/houston
Apple Push Notifications; No Dirigible Required
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓30+ active contributors
- ✓Distributed ownership (top contributor 47% of recent commits)
- ✓MIT licensed
Show 3 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Stale — last commit 5y ago
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/nomad-cli/houston)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/nomad-cli/houston on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: nomad-cli/houston
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/nomad-cli/houston shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- 30+ active contributors
- Distributed ownership (top contributor 47% of recent commits)
- MIT licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 5y ago
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live nomad-cli/houston
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/nomad-cli/houston.
What it runs against: a local clone of nomad-cli/houston — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in nomad-cli/houston | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 1815 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of nomad-cli/houston. If you don't
# have one yet, run these first:
#
# git clone https://github.com/nomad-cli/houston.git
# cd houston
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of nomad-cli/houston and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "nomad-cli/houston(\\.git)?\\b" \\
&& ok "origin remote is nomad-cli/houston" \\
|| miss "origin remote is not nomad-cli/houston (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "lib/houston.rb" \\
&& ok "lib/houston.rb" \\
|| miss "missing critical file: lib/houston.rb"
test -f "lib/houston/client.rb" \\
&& ok "lib/houston/client.rb" \\
|| miss "missing critical file: lib/houston/client.rb"
test -f "lib/houston/connection.rb" \\
&& ok "lib/houston/connection.rb" \\
|| miss "missing critical file: lib/houston/connection.rb"
test -f "lib/houston/notification.rb" \\
&& ok "lib/houston/notification.rb" \\
|| miss "missing critical file: lib/houston/notification.rb"
test -f "houston.gemspec" \\
&& ok "houston.gemspec" \\
|| miss "missing critical file: houston.gemspec"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 1815 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1785d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/nomad-cli/houston"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Houston is a Ruby gem that sends Apple Push Notifications (APNs) using the legacy binary APNs protocol. It wraps the complexity of APNs certificate handling, notification serialization, and socket communication into a simple API where you construct a Houston::Notification object and call client.push(). However, it is deprecated—Apple discontinued the binary protocol on March 31, 2021, and this gem is no longer maintained. Single-gem structure: lib/houston.rb is the entry point; lib/houston/client.rb handles connection and credential management; lib/houston/connection.rb manages the socket-level APNs protocol; lib/houston/notification.rb models the push payload. bin/apn provides a CLI interface. Tests in spec/ use RSpec.
👥Who it's for
iOS app developers and backend engineers who need to send push notifications from Ruby on Rails or other Ruby applications. Specifically, those integrating with background job queues (Resque, queue_classic) to schedule or batch notification delivery. This is now primarily of historical interest or for maintaining legacy systems.
🌱Maturity & risk
This project is abandoned and deprecated. While it has a clean codebase (29K lines of Ruby, with test coverage via spec/ and CI via .travis.yml), Apple officially discontinued the binary APNs protocol it relies on as of March 31, 2021. The README explicitly states 'This project is no longer being maintained.' Do not use for new projects.
Critical risk: the underlying APNs binary protocol is no longer supported by Apple and will not function. There are no recent commits (implied by deprecation notice), no active maintenance, and no migration path within this gem. Dependency management is outdated (Gemfile-based, likely Ruby 1.9–2.x era). Using this in production will fail when Apple's legacy gateway shuts down or if it already has.
Active areas of work
Nothing. The project is unmaintained. The README directs users to migrate to alternative libraries like Apnotic (which supports APNs2 / HTTP/2 protocol). No active development, PRs, or releases expected.
🚀Get running
git clone https://github.com/nomad-cli/houston.git
cd houston
bundle install
bundle exec rspec
Daily commands:
This is a library, not a service. Test the codebase with bundle exec rspec. See bin/apn for a CLI example; invoke via bundle exec bin/apn --help (though it may not be fully functional without a valid certificate and device token).
🗺️Map of the codebase
lib/houston.rb— Main entry point that requires and exposes the Houston module interface for sending push notifications.lib/houston/client.rb— Core client class that manages APNs connection credentials and provides the primary send() method for notifications.lib/houston/connection.rb— Handles the low-level SSL socket connection to Apple's APNs servers using binary protocol.lib/houston/notification.rb— Data model representing a single push notification with device token, alert, badge, and sound properties.houston.gemspec— Gem manifest defining dependencies and package metadata that contributors must understand for local development.bin/apn— Command-line interface entry point for sending notifications from shell scripts or CI/CD pipelines.
🧩Components & responsibilities
- Houston::Client (Ruby, OpenSSL) — Manages APNs credentials, establishes connection, and orchestrates notification sending
- Failure mode: Certificate invalid/expired: raises exception; timeout: blocks indefinitely; APNs unavailable: TCP connection refused
- Houston::Connection (Ruby Socket, OpenSSL) — Low-level SSL socket lifecycle and binary protocol frame encoding/decoding
- Failure mode: SSL handshake failure: exception; malformed frame: APNs rejects; socket closed: reconnect needed
- Houston::Notification (Ruby, binary packing) — Encapsulates push notification payload (device_token, alert, badge, sound) and binary encoding
- Failure mode: Invalid device token format: APNs rejection; oversized alert: frame truncation
🔀Data flow
CLI or application code→Houston::Client— Pass certificate path and credentials; request send(notification)Houston::Client→Houston::Connection— Provide SSL certificate and APNs gateway address; request connection establishmentHouston::Connection→Houston::Notification— Request binary-encoded APNs frame payloadHouston::Connection→Apple APNs Server— Send binary notification frame over SSL; receive ACK or error responseApple APNs Server→iOS Device— Route notification to device by token; trigger local notification display
🛠️How to make changes
Send a Push Notification
- Instantiate a Client with your certificate path and optional default gateway/port (
lib/houston/client.rb) - Create a Notification object with device_token, alert text, badge count, and sound (
lib/houston/notification.rb) - Call client.send(notification) to deliver the notification via APNs (
lib/houston/client.rb)
Add a New Notification Property
- Add the new attribute as an attr_accessor in Notification class (
lib/houston/notification.rb) - Update the payload encoding logic in Notification to include the property in the APNs binary frame (
lib/houston/notification.rb) - Add test case covering the new property in the notification spec (
spec/notification_spec.rb)
Integrate Houston into a Background Job Queue
- Create a job class that instantiates Houston::Client with persisted credentials (
lib/houston/client.rb) - In the job perform method, deserialize the Notification and call client.send(notification) (
lib/houston/notification.rb) - Enqueue the job from your Rails controller or event handler with notification data
🔧Why these technologies
- Ruby gem — Lightweight wrapper around system OpenSSL for easy integration into Ruby/Rails applications
- Binary APNs protocol (legacy) — Direct SSL socket communication with Apple's APNs service for direct control over notification delivery
- RSpec testing — Industry-standard Ruby testing framework for unit and integration tests
⚖️Trade-offs already made
-
Synchronous send() method with blocking socket I/O
- Why: Simple request-response semantics match APNs binary protocol interaction
- Consequence: Caller must handle timeouts and implement queuing for production use; recommended to use with background job systems
-
No built-in feedback service integration
- Why: Minimalist design focuses on send-only delivery
- Consequence: Cannot detect invalid tokens or uninstalled apps; users must implement separate feedback socket polling
-
Legacy binary protocol only (no HTTP/2 APNs2)
- Why: Stable and well-understood at time of development
- Consequence: Deprecated as of March 31, 2021; users must migrate to Apnotic or similar for modern APNs2 support
🚫Non-goals (don't propose these)
- No HTTP/2 APNs2 protocol support (legacy binary protocol only)
- No feedback service implementation for token validation
- No built-in background job scheduling or queuing
- No authentication method support beyond certificate-based SSL
- No batch notification optimization or retry logic
- No macOS/tvOS/watchOS push support (iOS APNs only)
📊Code metrics
- Avg cyclomatic complexity: ~2.5 — Minimal abstraction layers; straightforward request-response protocol; no complex state management or conditional logic
- Largest file:
lib/houston/connection.rb(120 lines) - Estimated quality issues: ~3 — Lack of timeout handling, limited error messaging, and no connection pooling; deprecated APNs protocol; project officially unmaintained as of March 2021
⚠️Anti-patterns to avoid
- Synchronous blocking I/O in notification send (High) —
lib/houston/client.rb: send() method blocks on socket read waiting for APNs response; no timeout or async handling means long-running requests can stall application threads - No connection pooling or reuse (Medium) —
lib/houston/connection.rb: Each send() call creates a new SSL connection to APNs; inefficient for high-volume sending; recommend persistent connection pool - Silent error suppression in binary frame reading (Medium) —
lib/houston/connection.rb: Socket read failures or malformed APNs responses may not propagate clear error messages to caller - No retry or exponential backoff logic (Low) —
lib/houston/client.rb: Failed sends are not retried; caller must implement retry logic externally
🔥Performance hotspots
lib/houston/connection.rb(Network I/O) — SSL handshake latency (100–500ms per send) due to new connection per notification; exacerbated by lack of connection poolinglib/houston/notification.rb(CPU) — Payload encoding logic performs binary packing in Ruby; negligible for single notifications but cumulative in bulk sendinglib/houston/client.rb(Concurrency) — Synchronous blocking send() call; limits throughput in single-threaded contexts; must be wrapped in job queue for production
🪤Traps & gotchas
Deprecated protocol: the binary APNs protocol this gem implements was shut down by Apple on March 31, 2021; calls to client.push() will fail against current Apple infrastructure. Certificate format: expects .pem files (PKCS#12 or PEM-encoded); mismatched or expired certificates will silently fail to connect. Environment variables: reads APN_GATEWAY_URI, APN_CERTIFICATE, etc.; if set incorrectly, behavior is unpredictable. No error handling in examples: the README shows simple client.push(notification) with no error or response handling—actual APNs delivery is fire-and-forget at the network layer. Token format: device tokens must be exact (48 hex characters or similar); whitespace is common in copy-paste but breaks delivery.
🏗️Architecture
💡Concepts to learn
- Binary APNs Protocol — Houston implements Apple's legacy binary APNs protocol (RFC-style framing, TLS sockets, binary notification format); understanding this is key to reading
lib/houston/connection.rband recognizing why the gem is now obsolete - TLS Certificate Pinning & Client Certificates — Houston loads a
.pemcertificate to authenticate to Apple's APN gateway via mutual TLS; this is critical to understanding why certificate expiry or format errors break the entire system - Device Token Registration — APNs uses device tokens (64-character hex strings) to target individual iOS devices; Houston's
Notification#device=parameter expects this format, and token mismatch or staleness is a silent failure mode - Payload Serialization & Limits — Houston serializes notification properties (alert, badge, custom_data) into a binary packet with strict size limits (256 bytes in legacy protocol); exceeding limits fails silently; this is implemented in
lib/houston/notification.rb - Fire-and-Forget Delivery Model — The binary APNs protocol does not provide delivery receipts or error feedback for individual notifications; Houston sends and closes the connection, leaving no way to know if a push was delivered—this is a fundamental limitation you must work around in production
- Environment-Specific Gateways (Sandbox vs. Production) — iOS development and production use different APNs gateways (gateway.sandbox.push.apple.com vs. gateway.push.apple.com); Houston's
Client.developmentandClient.productionclass methods route to the correct endpoint - Background Job Integration Patterns — Houston is designed to be called from background workers (Resque, queue_classic) to avoid blocking web request handlers; understanding this pattern is critical to using it correctly in Rails apps, though now deprecated
🔗Related repos
ostinelli/apnotic— The recommended modern replacement—supports APNs2 (HTTP/2) protocol which Apple still actively supports, drop-in migration target for Houston usersnomad-cli/nomad— Sister project in the same nomad-cli organization; likely uses Houston for iOS app distribution workflowsfastlane/fastlane— Ecosystem companion; Houston README explicitly recommends fastlane pem for certificate generation and managementruby-on-rails/rails— Primary ecosystem—Houston is typically integrated into Rails apps as middleware for background job notification delivery (Resque/queue_classic)rpush/rpush— Alternative Ruby gem for multi-protocol push (APNs, GCM, MPNS); provides more features than Houston but has similar maintenance challenges for the APNs side
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add deprecation warning and migration guide to Houston CLI and library entry points
The README prominently notes Houston is no longer maintained and the legacy binary APNs protocol is unsupported as of March 31, 2021. However, users installing the gem via bin/apn or lib/houston.rb won't see this critical warning until they read the README. Adding a deprecation notice to the actual code entry points (bin/apn and lib/houston.rb) will ensure users are immediately informed and directed to alternatives like Apnotic before they invest time integrating the library.
- [ ] Add a deprecation warning to bin/apn that prints to STDERR on execution
- [ ] Add a deprecation warning to lib/houston.rb that is triggered on require
- [ ] Include a link to the migration guide in both warnings
- [ ] Add integration tests in spec/ to verify warnings are emitted (new file: spec/deprecation_spec.rb)
Add comprehensive error handling and connection failure tests in spec/client_spec.rb
The spec file structure shows spec/client_spec.rb exists but the repo handles APNs connections via lib/houston/connection.rb. There are likely untested edge cases around connection failures, certificate validation errors, and timeout scenarios that users encounter in production. Adding tests for these scenarios will help new contributors understand expected behavior and prevent regressions.
- [ ] Add test cases for invalid certificate/key scenarios in spec/client_spec.rb
- [ ] Add test cases for connection timeout and socket errors
- [ ] Add test cases for malformed or expired credentials
- [ ] Mock APNs server responses in spec/notification_spec.rb to test delivery failures and feedback loops
Extract and document certificate validation logic from lib/houston/connection.rb into a dedicated module
Certificate management is critical for APNs but scattered across connection logic. Extracting this into lib/houston/certificate.rb or lib/houston/credentials.rb will improve maintainability and make it easier for contributors to understand credential handling. This also enables better testing and documentation of the certificate loading process.
- [ ] Create lib/houston/certificate.rb to encapsulate certificate loading and validation
- [ ] Move certificate-related code from lib/houston/connection.rb into the new module
- [ ] Update lib/houston/client.rb to use the new certificate module
- [ ] Add unit tests in spec/certificate_spec.rb covering PEM parsing, expiration checks, and validation errors
🌿Good first issues
- Add migration guide to APNs2 in README: The README warns users to migrate but does not provide step-by-step instructions or code examples for switching to Apnotic. Create a 'Migration Guide' section showing how to rewrite a Houston
push()call using Apnotic. - Document certificate generation workflow with fastlane pem: The README mentions fastlane pem but does not explain how to use it or where to get the resulting
.pemfile. Add a concrete example:fastlane pemcommand, file location, and how to pass it to Houston::Client. - Add specs for connection failures and retry logic:
spec/client_spec.rbdoes not test socket errors, timeouts, or reconnection behavior. Add test cases for network failures (e.g., timeout, certificate rejection) to document expected behavior or identify missing error handling.
⭐Top contributors
Click to expand
Top contributors
- @mattt — 47 commits
- @dankimio — 17 commits
- @mpvosseller — 3 commits
- @amatsuda — 2 commits
- @KrauseFx — 2 commits
📝Recent commits
Click to expand
Recent commits
2455d5d— Update README.md (mattt)162a228— Update README.md (mattt)f6c0092— Add .travis.yml config (dankimio)07838ba— Add Travis shield to README.md (dankimio)47bd9b2— Add CHANGELOG.md (#167) (dankimio)a109511— Update code style (dankimio)408f890— Bump RSpec to 3.8 (dankimio)4b122bb— Adds -r option to binary to pass the raw certificate (#159) (Johann)03eae1e— Bump version to 2.4.0 (mattt)4c073e3— Add thread-id to aps (#163) (RebMeir)
🔒Security observations
This codebase presents significant security concerns. The project is unmaintained and relies on a deprecated APNs protocol that has been unsupported since March 2021. Critical vulnerabilities include lack of ongoing security updates, use of legacy communication protocols, and incomplete dependency visibility. The unmaintained status makes this library unsuitable for production use. Immediate migration to a maintained alternative like Apnotic is strongly recommended.
- Critical · Unmaintained Project Using Deprecated Protocol —
README.md, Overall Project Status. The project is officially marked as unmaintained and uses only the legacy binary APNs protocol, which has been unsupported since March 31, 2021. This creates significant security risks as the library will not receive security updates and uses deprecated/vulnerable communication protocols for Apple Push Notifications. Fix: Migrate to maintained alternatives such as Apnotic or other libraries supporting the modern APNs2 protocol. Do not use this library in production environments. - High · Missing Dependency Information —
Gemfile, houston.gemspec. The Gemfile content was not provided for analysis. Without visibility into dependencies, it's impossible to identify vulnerable gem versions, outdated packages, or known security issues in transitive dependencies. Fix: Provide complete Gemfile and Gemfile.lock contents. Run 'bundle audit' regularly to check for known vulnerabilities in dependencies. Ensure all gems are pinned to secure versions. - High · Potential Certificate/Credentials Exposure Risk —
lib/houston/client.rb, lib/houston/connection.rb. Houston requires Apple Push Notification certificates and credentials to function. The codebase structure suggests these credentials are passed to the client, but without reviewing the actual implementation in lib/houston/client.rb and lib/houston/connection.rb, it's unclear if credentials are properly protected, encrypted, or if there are hardcoded examples. Fix: Ensure certificates and credentials are: (1) Never hardcoded, (2) Loaded from secure environment variables or secure vaults, (3) Not logged or exposed in error messages, (4) Properly validated before use. - Medium · No Visible Security Tests —
spec/. The spec directory contains basic tests (client_spec.rb, notification_spec.rb) but no visible security-focused test cases for credential handling, injection protection, or protocol security. Fix: Add security-focused unit and integration tests covering: credential injection, malicious payload handling, SSL/TLS certificate validation, and secure credential storage patterns. - Medium · Legacy Protocol Security Concerns —
lib/houston/connection.rb. The legacy binary APNs protocol has known security limitations compared to the modern HTTP/2-based APNs2 protocol, including weaker encryption options and lack of modern security enhancements. Fix: Migrate to APNs2 protocol which provides stronger security guarantees, better error handling, and ongoing security maintenance.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.