Class ServerPushKeys

java.lang.Object
com.iizix.server.push.engine.ServerPushKeys

public final class ServerPushKeys extends Object
The server's own Push Notification (PN) configuration: the .iiziPush container that serves the subscriptions made with application ID *, i.e. the iiziRun clients that have chosen no application.

Why the server needs a key pair of its own. A Browser subscribes with exactly one applicationServerKey - pushManager.subscribe takes one, and the endpoint it returns is bound to it - so the * subscriptions need one VAPID pair, and it cannot be any application's: the server has no application selected at that point, and the iiziSystem application carries none. A server whose settings carry no $pushNotifications container must still be able to answer /iizi-push/keys for *, so the pair is acquired here: generated on first use with VAPIDKeys.generateKeys() and persisted through the server's own property factory, in FILE_NAME beside the spool.

Precedence. An operator may configure the server's container by hand as the $push child of the $pushNotifications settings container. When that child carries a valid VAPID pair it is used as it is. When it exists without a valid pair, its other members - the VAPID subject and the Firebase configuration - are kept and the generated pair fills the two keys. When there is no such child, the generated container serves alone.

The credentials file's [server] section (PUSH-CREDENTIALS.md section 3.6, ruled 2026-09-26) sits above both, member by member (S-4): a member present there wins, an absent one falls through to the $push child, and the VAPID pair falls through once more to the generated pair. Its fcmServiceAccountFile names Google's key file, whose text the credentials read when a send needs it and hand over here as the fcmServiceAccount member; a file that cannot be read leaves that member empty, which the FCM transport reports as not configured. The section is read on every call, so an edit of the file takes effect at the next lookup, as the settings do.

Resolved at use. The configured child is read from the live settings on every call, so an edit takes effect without a restart, as it does for a hot-deployed application. The generated container is re-read when its file's modification time changes, so an operator may replace the file; it is otherwise held, because it is the server's own and nothing deploys it.

Key files (PUSH-KEYFILES-DESIGN.md sections 9 and 9.3). The section's VPrivKeyFile is read by the credentials and arrives here as the section's pair; a named file that cannot be used still wins, leaving VAPID for * not configured. A settings container that names a VPrivKeyFile beside its public key carries a pair for this class, and the application configurations resolve the file. The generated file is written owner-only (mode 0600 where POSIX applies) and, when found with a broader mode, is narrowed by the server itself with a WARNING - it is the server's own file - never refused.

Fail closed. A file that exists but cannot be loaded, or holds no valid pair, is never overwritten: the pair in it may be the one every existing * subscription is bound to. The failure is logged SEVERE and get() answers null, which the endpoints report as "not configured", until the operator repairs or removes the file. The same holds when a freshly generated pair cannot be written: a pair that lives only in memory would bind subscriptions that die at the next restart, which is worse than a clear refusal now.

Rotation (rotate()) generates and persists a new pair. Every existing * Browser subscription is bound to the old public key and the push services will refuse sends signed with the new one, so a rotation is a deliberate act that invalidates them; it is offered for an administration surface and has no caller in the server today.

Thread-safe.

Author:
Christopher Mindus
  • Field Details

    • FILE_NAME

      public static final String FILE_NAME
      The file name of the generated container, in the spool directory: "server-keys.iiziPush".
      See Also:
  • Constructor Details

    • ServerPushKeys

      public ServerPushKeys(Supplier<PushProps> configured, File file, ServerPushKeys.Storage storage)
      Constructor. Nothing is read until get().
      Parameters:
      configured - Supplies the operator-configured container from the live settings, or null; called on every get().
      file - The file of the generated container.
      storage - The XML storage.
    • ServerPushKeys

      public ServerPushKeys(Supplier<PushProps> configured, Supplier<PushServerSection> ini, File file, ServerPushKeys.Storage storage)
      Constructor with the credentials file's [server] section. Nothing is read until get().
      Parameters:
      configured - Supplies the operator-configured container from the live settings, or null; called on every get().
      ini - Supplies the [server] section of the credentials file, or null when none is wired; called on every get().
      file - The file of the generated container.
      storage - The XML storage.
  • Method Details

    • getFile

      public File getFile()
      Gets the file of the generated container.
      Returns:
      The file.
    • get

      public PushProps get()
      Gets the server's container: the [server] section's members over the configured container's over the generated pair (S-4). Without a section, the configured one when it carries a VAPID pair - inline, or a public key beside a VPrivKeyFile that the application configurations resolve - else the generated pair merged into the configured container when there is one, else the generated container alone.
      Returns:
      The container, or null when no pair is available: the file exists but cannot be used, or a generated pair could not be persisted. Both are logged.
    • rotate

      public PushProps rotate()
      Generates and persists a new pair, replacing the generated container.
      Returns:
      The new container, or null when it could not be persisted (logged).
    • isMatchingPair

      public static boolean isMatchingPair(String publicKey, String privateKey)
      Checks that a VAPID pair belongs together: a signature made with the private key verifies under the public key (SHA256withECDSA, java.base only), the check the push services make at the first send. Either key failing to load is a mismatch.
      Parameters:
      publicKey - The public key, base64url.
      privateKey - The private key, base64url.
      Returns:
      true when the public key is the private key's.