--- title: "1.15.8 — Stable outbound IPs for provider allowlists" description: "Providers that require IP allowlisting need a fixed set of addresses, and a fleet whose nodes take whatever public IP AWS hands them cannot supply one. This release adds a Stable outbound IPs control to the Admin fleet screen: one confirmed action gives every registered node a permanent Elastic IP,…" date: "2026-08-20" tags: ["release"] canonical: "https://django-mojo.sitesmojo.com/changelog/1-15-8/" --- Providers that require IP allowlisting need a fixed set of addresses, and a fleet whose nodes take whatever public IP AWS hands them cannot supply one. This release adds a Stable outbound IPs control to the Admin fleet screen: one confirmed action gives every registered node a permanent Elastic IP, verifies the association against AWS before reporting success, and shows the canonical list to hand vendors. The policy is durable, so a node added later gets its address before it serves traffic. Disabling is deliberate and recoverable — it detaches but keeps the reservations, so an accidental click cannot discard addresses a vendor has already allowlisted. Installs with no load balancer, which this control does not manage, now get a read-only list of the addresses their nodes already hold instead of an empty panel. Also: deploys no longer risk installing a stale framework version from a cached PyPI catalog, and three admin-portal fixes. ### Added - **Stable outbound IPs** — an Admin fleet control that gives every registered application node a permanent Elastic IP, so third-party providers can allowlist a fixed set of addresses. The panel shows the canonical address list to give vendors. - `enable_stable_ips` and `disable_stable_ips` actions on `POST /api/aws/capacity/apply`, behind the same gate as the rest of capacity: a literal superuser with `manage_aws`, an interactive session, fresh authentication, and a typed confirmation. - `AWS_STABLE_OUTBOUND_IPS`, a protected system setting holding the policy. It is durable, so `add_node` attaches a stable address to a new node before registering it behind the balancer. - An `egress` block on the capacity report: the vendor allowlist, reserved addresses, how many enabling would allocate, and per-node `public_ip` and `stable_ip`. - A read-only `egress.fallback_attached` list for installs with no load balancer, so a single-node estate still shows the address to give providers instead of an empty panel. - `stable_node_ips` in provisioning (env-file key and `--stable-node-ips`), which keeps per-node Elastic IPs when a load balancer is also built. - Elastic IP primitives on the EC2 helper: `address_map`, `allocate_address`, `associate_address`, `disassociate_address`, `tag_resources`. ### Changed - Fleet-wide capacity actions now ignore a caller-supplied `resource`; the typed `confirm_resource` echo is always the action word. A custom caller that sent its own resource for `add_node` and echoed that value must now echo `add_node`. - `add_node` gains an `addressing` phase when the policy is on. It fails closed: a node that cannot get its stable address is left running and unregistered rather than serving from an address no provider has allowlisted. - Admin tab titles read ` - ` instead of a static "MOJO Admin", so several open tabs are tellable apart. - The GitHub workflow file and the reveal-once deploy secret render as read-only code blocks instead of bare form textareas. ### Fixed - Deploys and first-boot installs refresh the django-mojo catalog entry before installing. pip 26.2 began honoring PyPI's Simple API cache lifetime, so a framework version published moments earlier could be missing from a cached catalog response even though its wheel was already live — a deploy would then install the previous version and report success. Older pip releases, which always revalidate, are unaffected and skip the unsupported option. - Admin portal list rows navigate when clicked anywhere on the row, not only from the small link at the far right. ### Upgrade notes - The stable-outbound control needs four more IAM actions: `ec2:DescribeAddresses`, `ec2:AllocateAddress`, `ec2:AssociateAddress`, `ec2:DisassociateAddress`. Without them the section reports the denial and offers no controls rather than showing an empty address list. - Enabling adds no monthly cost while the addresses stay attached — an Elastic IP replaces the identical charge for the node's auto-assigned IPv4. The cost appears on **disable**, where each kept reservation bills alongside the node's new auto-assigned address. - Disabling leaves each node without a public address for up to several minutes, and indefinitely if its network interface was not created with auto-assign. Outbound provider calls fail during that window. - Ownership tags are not scoped by project or environment: two django-mojo installations sharing one AWS account and region can consume each other's reserved addresses. Run one installation per account and region, or do not rely on reserve-and-re-enable across them. - The catalog-refresh fix ships **in** this release, so the deploy that installs 1.15.8 still runs the old behavior; the protection applies from the next deploy onward.