Configuration
Heimdall has two configuration surfaces: server registration settings and the browser runtime object exposed as window.Heimdall.
Use this page when you need the exact defaults, the mutable runtime properties, and the timing rules for when a setting must be applied.
1. Configuration Surfaces
Most application configuration happens on the server. Browser behavior that is intentionally runtime-tunable lives under Heimdall.config after the JavaScript bundle loads.
Server:
builder.Services.AddHeimdall(options => { ... });
app.UseHeimdall();
Browser:
window.Heimdall
window.Heimdall.config2. Server Settings
AddHeimdall accepts HeimdallServiceSettings. These settings affect server endpoint behavior, error detail, and Bifrost topic authorization.
builder.Services.AddHeimdall(options =>
{
options.EnableDetailedErrors = builder.Environment.IsDevelopment();
options.BifrostTopicPolicy = "BifrostTopic";
options.AuthorizeBifrostTopic = (ctx, topic) =>
ValueTask.FromResult(topic.StartsWith("user:"));
options.BifrostHeartbeatInterval = TimeSpan.FromSeconds(15);
});3. Server Defaults
These are the framework defaults from HeimdallServiceSettings.
EnableDetailedErrors:
false
BifrostTopicPolicy:
null
AuthorizeBifrostTopic:
null
BifrostHeartbeatInterval:
15 seconds4. Browser Runtime Object
After the bundle loads, Heimdall assigns a global runtime object to window.Heimdall. Its methods are the supported browser-side API surface.
Heimdall.apiVersion
Heimdall.invoke(actionId, payload, options)
Heimdall.boot(root)
Heimdall.onReady(callback)
Heimdall.clearCsrfToken()
Heimdall.sse.connect(topic, options)
Heimdall.sse.disconnect(element)
Heimdall.sse.disconnectAll()
Heimdall.config
Heimdall._observerOOB Default
Out-of-band response directive processing is enabled by default. The runtime default is Heimdall.config.oobEnabled = true. Set it to false only when the application wants to ignore <invocation> directives returned by actions or SSE payloads.
5. Set Runtime Config
The runtime creates Heimdall.config when the script loads. Put configuration immediately after the runtime script, and put early-boot options before DOMContentLoaded or before any manual boot call.
<script src="/_content/HeimdallFramework.Web/heimdall-bundle.min.js"></script>
<script>
Heimdall.config.debug = true;
Heimdall.config.inputDebounceMs = 350;
Heimdall.config.hoverDelayMs = 200;
Heimdall.config.oobEnabled = true;
</script>6. Runtime Config Defaults
These are the current browser runtime defaults from startup.js.
Heimdall.config = {
basePath: "/__heimdall",
apiVersion: 1,
endpoints: {
contentActions: "/__heimdall/v1/content/actions",
csrf: "/__heimdall/v1/csrf",
bifrostToken: "/__heimdall/v1/bifrost/token",
bifrost: "/__heimdall/v1/bifrost"
},
observeDom: true,
debug: false,
authReturnUrlParameter: "ReturnUrl",
inputDebounceMs: 250,
hoverDelayMs: 150,
scrollThresholdPx: 120,
scrollMinIntervalMs: 250,
visibleRootMargin: "0px",
visibleThreshold: 0,
oobEnabled: true,
requestSync: "parallel",
requestTimeoutMs: 0,
sseDefaultSwap: "none",
sseEventName: "heimdall",
sseReconnectDelayMs: 250,
sseReconnectMaxDelayMs: 10000,
sseReconnectBackoffFactor: 2,
sseSweepIntervalMs: 5000,
ssePauseWhenHidden: false
};7. Endpoint and Auth Redirect Configuration
Endpoint values are read when the runtime makes a request. The default paths match app.UseHeimdall(). Only change them when the server has been mapped behind a matching path or proxy.
Heimdall.config.endpoints.contentActions = "/internal/heimdall/actions";
Heimdall.config.endpoints.csrf = "/internal/heimdall/csrf";
Heimdall.config.endpoints.bifrostToken = "/internal/heimdall/bifrost/token";
Heimdall.config.endpoints.bifrost = "/internal/heimdall/bifrost";Content actions send POST requests with the action id in the query string and X-Heimdall-Content-Action header. CSRF and Bifrost token requests are same-origin GET requests.
When cookie authentication redirects an action or Bifrost subscribe-token request to sign-in, Heimdall rewrites the configured return URL query parameter to the current page. The default is ReturnUrl, matching ASP.NET Core cookie auth. Matching is case-insensitive, so returnUrl and ReturnUrl are both recognized, and Heimdall preserves the casing used by the server redirect URL.
// ASP.NET Core default:
options.ReturnUrlParameter = "ReturnUrl";
// Heimdall default:
Heimdall.config.authReturnUrlParameter = "ReturnUrl";
// If your app uses a custom name:
options.ReturnUrlParameter = "next";
Heimdall.config.authReturnUrlParameter = "next";8. Trigger Timing Defaults
These config values supply runtime defaults. Per-element attributes and programmatic invocation options still win when they are supplied.
Heimdall.config.inputDebounceMs
-> default for heimdall-content-input
-> overridden by heimdall-debounce
Heimdall.config.hoverDelayMs
-> default for heimdall-content-hover
-> overridden by heimdall-hover-delay
Heimdall.config.scrollThresholdPx
-> near-end scroll distance
-> overridden by heimdall-scroll-threshold
Heimdall.config.scrollMinIntervalMs
-> minimum time between scroll-triggered actions
Heimdall.config.requestSync
-> default overlap strategy: parallel
-> overridden by heimdall-sync or invoke({ sync })
Heimdall.config.requestTimeoutMs
-> client request timeout: 0 means no timeout
-> overridden by invoke({ timeoutMs })9. Boot and DOM Observation
The runtime boots document boundaries on ready. observeDom installs a MutationObserver so newly inserted Heimdall markup is activated automatically.
Heimdall.config.observeDom = true;
// Manual boot is useful when observeDom is false,
// or when an external renderer inserts markup.
Heimdall.boot(document.querySelector("#new-region"));10. Visible Trigger Timing
visibleRootMargin and visibleThreshold are read once when the IntersectionObserver is first created. Set them before visible elements are booted.
<script src="/_content/HeimdallFramework.Web/heimdall-bundle.min.js"></script>
<script>
Heimdall.config.visibleRootMargin = "200px 0px";
Heimdall.config.visibleThreshold = 0.1;
</script>11. OOB Configuration
OOB processing controls whether returned invocation directives can update other DOM regions outside the main target. Redirect and abort directives are still response directives, but invocation processing is gated by oobEnabled.
// Default: true
Heimdall.config.oobEnabled = true;
// Disable invocation directive processing.
Heimdall.config.oobEnabled = false;12. SSE Configuration
Bifrost SSE uses global defaults for event name, default swap mode, reconnect behavior, sweep timing, and hidden-tab pause behavior. Subscriber attributes can override event, target, swap, and disable behavior per element.
Heimdall.config.sseDefaultSwap = "none";
Heimdall.config.sseEventName = "heimdall";
Heimdall.config.sseReconnectDelayMs = 250;
Heimdall.config.sseReconnectMaxDelayMs = 10000;
Heimdall.config.sseReconnectBackoffFactor = 2;
Heimdall.config.sseSweepIntervalMs = 5000;
Heimdall.config.ssePauseWhenHidden = false;Per-subscriber HTML attributes:
heimdall-sse="orders"
heimdall-sse-topic="orders"
heimdall-sse-target="#orders"
heimdall-sse-swap="beforeend"
heimdall-sse-event="order.updated"
heimdall-sse-disable="true"13. Programmatic Invocation
Use Heimdall.invoke when a browser script needs to run the same content-action request pipeline as a DOM trigger. It supports the same synchronization coordinator, an optional client timeout, and an external AbortSignal.
const controller = new AbortController();
const result = await Heimdall.invoke(
"orders.refresh",
{ page: 2 },
{
target: "#orders",
swap: "inner",
headers: { "X-Request-Source": "toolbar" },
sync: "replace",
syncGroup: "orders",
timeoutMs: 5000,
signal: controller.signal,
onSuccess: result => console.log(result.ms),
onError: result => console.warn(result.status)
});
// Expected cancellation resolves normally.
if (result.cancelled) {
console.log(result.cancelReason);
}14. Programmatic SSE
Use Heimdall.sse.connect when a script needs to subscribe without a declarative heimdall-sse attribute. The element anchors lifecycle validation for that subscription.
const subscription = Heimdall.sse.connect("orders", {
element: document.querySelector("#orders-panel"),
target: "#orders",
swap: "beforeend",
event: "order.updated"
});
subscription.close();
Heimdall.sse.disconnect("#orders-panel");
Heimdall.sse.disconnectAll();15. Request, Swap, and Runtime Events
Heimdall exposes a dependency-free integration surface through bubbling DOM CustomEvents. Request configuration is mutable, request-before and swap-before are cancellable with preventDefault(), and finally always fires. Declarative actions dispatch from the triggering element; programmatic and SSE work remains observable on document.
Heimdall.config.debug = true;
document.addEventListener("heimdall:request-config", event => {
event.detail.headers["X-Correlation-ID"] = crypto.randomUUID();
event.detail.sync = "replace";
event.detail.syncGroup = "search";
});
document.addEventListener("heimdall:request-before", event => {
// event.preventDefault() cancels before fetch.
});
document.addEventListener("heimdall:request-after", event => {});
document.addEventListener("heimdall:request-finally", event => {});
document.addEventListener("heimdall:request-cancel", event => {});
document.addEventListener("heimdall:request-timeout", event => {});
document.addEventListener("heimdall:swap-before", event => {
// Mutable: target, fragment, and swap.
// event.preventDefault() skips this swap.
});
document.addEventListener("heimdall:swap-after", event => {});
// Existing action/directive events remain supported.
document.addEventListener("heimdall:before", e => {});
document.addEventListener("heimdall:after", e => {});
document.addEventListener("heimdall:error", e => {});
document.addEventListener("heimdall:abort", e => {});
document.addEventListener("heimdall:redirect", e => {});
document.addEventListener("heimdall:javascript-error", e => {});
// SSE connection lifecycle events remain supported.
document.addEventListener("heimdall:sse-open", e => {});
document.addEventListener("heimdall:sse-close", e => {});
document.addEventListener("heimdall:sse-message", e => {});
document.addEventListener("heimdall:sse-error", e => {});
document.addEventListener("heimdall:sse-pause", e => {});
document.addEventListener("heimdall:sse-resume", e => {});
document.addEventListener("heimdall:sse-reconnect-scheduled", e => {});
document.addEventListener("heimdall:sse-abort", e => {});
document.addEventListener("heimdall:sse-redirect", e => {});16. When Changes Take Effect
Most values are read at the moment of use. A few bootstrapping values need to be set early because observers and sweep timers are created once.
Read on each request:
endpoints, debug, oobEnabled,
requestSync, requestTimeoutMs
Read from each trigger event:
inputDebounceMs, hoverDelayMs,
scrollThresholdPx, scrollMinIntervalMs
Set before first visible boot:
visibleRootMargin, visibleThreshold
Set before ready for automatic DOM observing:
observeDom
Set before SSE starts for initial sweeper interval:
sseSweepIntervalMs
Read during SSE reconnect/pause behavior:
sseReconnectDelayMs, sseReconnectMaxDelayMs,
sseReconnectBackoffFactor, ssePauseWhenHidden17. Practical Defaults
For most apps, keep the default endpoints, keep observeDom on, keep OOB on, and tune only the user-experience timings that your UI actually needs.
Common application-level tweaks:
Heimdall.config.debug = builderEnvironmentIsDevelopment;
Heimdall.config.inputDebounceMs = 300;
Heimdall.config.hoverDelayMs = 250;
Heimdall.config.requestSync = "parallel";
Heimdall.config.requestTimeoutMs = 0;
Heimdall.config.ssePauseWhenHidden = true;Next Steps
After configuration, the most useful references are the raw HTML attribute surface, the runtime behavior model, and the security boundaries around response processing.