feat(deploy): fetch options + per-deployment token helper

This commit is contained in:
Joseph Doherty
2026-06-26 12:28:48 -04:00
parent 8cb512ac51
commit a61865daa0
3 changed files with 82 additions and 0 deletions
@@ -0,0 +1,22 @@
using System.Security.Cryptography;
using System.Text;
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Deployment;
/// <summary>
/// Generates and compares the single-purpose, short-TTL token that authorizes a
/// site's HTTP fetch of one deployment's flattened config. URL-safe; compared in
/// constant time to avoid timing oracles.
/// </summary>
public static class DeploymentFetchToken
{
/// <summary>Generates a URL-safe random token (256 bits of entropy).</summary>
public static string Generate() =>
Convert.ToBase64String(RandomNumberGenerator.GetBytes(32))
.Replace('+', '-').Replace('/', '_').TrimEnd('=');
/// <summary>Constant-time string comparison (no early-out on first mismatch).</summary>
public static bool ConstantTimeEquals(string a, string b) =>
CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(a), Encoding.UTF8.GetBytes(b));
}
@@ -58,4 +58,17 @@ public class CommunicationOptions
/// <summary>Akka.Remote transport failure detection threshold.</summary>
public TimeSpan TransportFailureThreshold { get; set; } = TimeSpan.FromSeconds(15);
/// <summary>
/// Base URL (Traefik/LB) the SITE uses to fetch deploy configs from central,
/// e.g. "https://central.example:9000". Carried in RefreshDeploymentCommand so
/// sites need no new standing config. Empty disables notify-and-fetch fallback.
/// </summary>
public string CentralFetchBaseUrl { get; set; } = "";
/// <summary>
/// How long a staged PendingDeployment (and its fetch token) stays valid. Must
/// comfortably cover both site nodes' fetches within one deploy window.
/// </summary>
public TimeSpan PendingDeploymentTtl { get; set; } = TimeSpan.FromMinutes(5);
}