Open-source project
krakenjs/post-robot avatar
krakenjs/post-robot

post-robot turns window.postMessage into a request and response channel

Cross domain post-messaging on the client side using a simple listener/client pattern.

786 stars93 forksJavaScriptApache-2.0

At a glance

What is it?
post-robot is a small client-side library that lets two windows on different origins exchange messages and wait for answers, functions and errors. It is worth adopting for popup and embedded-frame workflows, but the documentation is thinner than the feature list suggests.
Who is it for?
post-robot fits teams that need a parent page to await a value from a popup or an embedded frame on another origin, and it fits poorly with projects that need audited origin policies, structured error codes or a published release trail.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 38 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A listener and client pattern layered over window.postMessage

post-robot sits on top of the browser's own window.postMessage and reshapes it into a request and response channel between two windows that may sit on different origins. The pattern has two halves. One window registers a handler for a message type with postRobot.on, and another calls that handler with postRobot.send, which hands back a promise. The README describes the intent in one line, calling it "Cross domain post-messaging on the client side, using a simple listener/client pattern." What separates it from a bare postMessage call is what travels back over the channel. The response event carries the data the listener returned, and the same promise also surfaces errors that stopped the message from getting through, so a failed cross-origin hop lands in a catch block rather than vanishing. The event object handed to both sides exposes source and origin, which means the receiving window can inspect who is talking to it before it answers.

The shape of that API suits the flows where a parent page must ask a third-party frame for something before it can continue: checkout popups, embedded editors, and embedded payment providers.

The listener answers with data and the sender waits on a promise

Registering a listener takes a message name, a handler, and nothing else. The handler returns whatever should travel back to the caller:

javascript
postRobot.on("getUser", function (event) {
  // Have it return some data to the calling window

  return {
    id: 1234,
    name: "Zippy the Pinhead",

    // Yep, we're even returning a function to the other window!

    logout: function () {
      return $currentUser.logout();
    },
  };
});

The caller then sends to a window reference and waits on the result:

javascript
postRobot
  .send(someWindow, "getUser", { id: 1337 })
  .then(function (event) {
    var user = event.data;

    console.log(event.source, event.origin, "Got user:", user);

    // Call the user.logout function from the other window!

    user.logout();
  })
  .catch(function (err) {
    // Handle any errors that stopped our call from going through

    console.error(err);
  });

Two handler styles are shown in the documentation. A plain callback that returns a value works, and an async handler is equally valid, so a handler can await its own data source before answering. The same holds for the caller, which can use the promise form or await the send call directly.

Functions, promises and errors are serialized, and the shapes change

The most unusual part of the library is that the payload is not limited to JSON. The README lists the data types it serializes and deserializes: objects, arrays, strings, numbers, booleans and null, plus any JSON-serializable type, functions, promises, error objects and regular expressions.

A function passed inside a payload is described as a reference to the original function, and the deserialized copy always returns a promise, specifically a ZalgoPromise from the krakenjs project. Deserialized promises arrive as ZalgoPromise instances too. The mechanism is that the call is messaged back to the window that owns the original function, executed there, and the result is messaged back again:

javascript
postRobot.send(myWindow, "getUser", { id: 1337 }).then(function (event) {
  var user = event.data;

  user.logout().then(function () {
    console.log("User was logged out");
  });
});

The practical consequence for the reader is that a remote function call is never synchronous. Because post-message is async underneath, user.logout() always returns a promise and has to be awaited. The same rewrapping applies to errors, which arrive as error objects rather than plain strings.

Naming both the window and the domain is what makes the channel safe

The documentation makes a specific security recommendation: always explicitly specify the window and domain you want to listen to and send messages to. That combination creates a message channel that only works between two windows on the stated domain. The scope can be narrowed on either side.

A listener can be bound to a single window reference:

javascript
postRobot.on("getUser", { window: window.parent }, function (event) {
  return {
    name: "Guybrush Threepwood",
  };
});

or to a single domain:

javascript
postRobot.on("getUser", { domain: "http://zombo.com" }, function (event) {
  return {
    name: "Manny Calavera",
  };
});

The sender accepts the same domain option in its fourth argument, and the secure channel version combines window and domain on the listener:

javascript
postRobot.on(
  "getUser",
  { window: childWindow, domain: "http://zombo.com" },
  function (event) {
    return {
      id: event.data.id,
      name: "Frodo",
    };
  }
);

Note that the domain values are written as full origins including the scheme. Matching is exact string matching against an origin, so http and https are different channels, and a subdomain is not the same origin as the parent domain.

Timeouts, one-off listeners and cancellation handle the awkward cases

A cross-origin call can fail silently, so the library lets the caller attach a timeout to a send:

javascript
postRobot
  .send(someWindow, "getUser", { id: 1337 }, { timeout: 5000 })
  .then(function (event) {
    console.log(event.source, event.origin, "Got user:", event.data.name);
  })
  .catch(function (err) {
    console.error(err);
  });

The same argument slot carries the domain option, so timeout and domain are peers in the options object rather than separate parameters. Listeners come in a one-off flavour as well, registered with postRobot.once so that it is removed after a single response, and every listener registration returns a handle with a cancel method:

javascript
var listener = postRobot.on("getUser", function (event) {
  return {
    id: event.data.id,
    name: "Zippy the Pinhead",
  };
});

listener.cancel();

This matters for single-page apps that navigate away from a view which registered a listener. Without the handle, a listener stays registered until the page unloads, and the documentation does not describe a global teardown routine.

IE popups still need a bridge iframe on the parent page

One failure mode is called out directly. The README states that IE blocks direct post messaging between a parent window and a popup on different domains, and that using post-robot in IE9 and later with popup windows requires setting up an invisible bridge iframe on the parent page. The diagram that illustrates this is truncated in the copy available here, but the repository carries three demo files that show the pattern: demo/parent.htm, demo/child.htm and demo/bridge.htm.

The bridge exists because the browser refuses the direct parent-to-popup hop, so an intermediate same-origin frame relays the traffic. That is not a configuration flag in post-robot itself, it is page structure the host application has to own and keep in sync on both sides of the popup. Modern browsers do not need it, which is another reason a team should decide which browsers the popup flow actually targets before picking this library.

The build layout shows a disciplined project with no published releases

The repository is organised like a package with a real release pipeline. The top level holds src/, dist/, test/, a demo directory, webpack.config.js, karma.conf.js, babel.config.js, a .flowconfig and a .eslintrc.js. The package manifest names the package @krakenjs/post-robot at version 11.0.1, with index.js as the main entry, and it defines separate scripts for lint, flow, format:check, karma, babel, webpack and build. The build script runs the full test chain first, and the prerelease script cleans, builds and checks the dist folder back into the repository before publishing.

Two facts are worth weighing against that tidy picture. The repository has no GitHub releases, so the changelog and npm tags are the only record of what changed between versions. And the last push was on 2026-09-14, which is recent enough to say the code is being touched, though the release trail itself is not visible on the releases page. The licence is Apache-2.0, which is permissive for commercial embedding, though the way functions from your page are executed in a peer window is worth reviewing on your side rather than treating as settled.

Where post-robot stops being the right tool

The clearest alternative is the raw browser API. window.postMessage with a manual origin check on the receiving side gives you a one-way channel and nothing else: no response payload, no function passing, no error propagation across origins, and no timeout helper. post-robot is the layer that adds all four, and the trade is that you accept its serialization rules and its exact-origin string matching.

Two other cases argue against it. First, if your payload includes a function, the call is executed in the window that owns the original function, and the caller receives a promise wrapper rather than a plain value, which breaks any code that expects a synchronous return. Second, the documentation says little about the error taxonomy: the README describes catching errors but does not enumerate error codes or shapes for a failed timeout versus a rejected listener, so a team with operational dashboards will have to build that taxonomy itself. For a parent page that needs one boolean from a popup, that overhead is real.

Editorial conclusion

post-robot fits teams that need a parent page to await a value from a popup or an embedded frame on another origin, and it fits poorly with projects that need audited origin policies, structured error codes or a published release trail. Before adopting it, check that the origins you must name are stable, confirm that the browser targets you need still allow the bridge iframe pattern for popups, and read the serialized payload types closely, because functions, promises and errors all change shape as they cross the boundary.

Frequently asked questions

How do I install post-robot in my project?

The README points to the npm package page for post-robot rather than spelling out install steps in prose. The package manifest names the package @krakenjs/post-robot at version 11.0.1, and its setup script installs the dependencies together with the flow-typed definitions.

How do I send a function to another window with post-robot?

Return the function from your listener, as the getUser example does with logout. The function runs on the window that defined it, and the deserialized copy always returns a promise, so the caller has to await or then it.

Does post-robot work between a parent window and a popup in Internet Explorer?

Not directly. IE blocks post messaging between a parent window and a popup on different domains, so the parent page has to set up an invisible bridge iframe, and the repository ships demo/bridge.htm alongside demo/parent.htm and demo/child.htm.

How do I set a timeout for a post-robot message?

Pass a timeout option as the fourth argument to send, for example { timeout: 5000 }. The same options slot also accepts a domain value, so a message can be scoped and time limited at once.

What is the difference between post-robot and using window.postMessage directly?

window.postMessage gives you a one-way channel plus a manual origin check on the receiving side. post-robot adds a listener and client pattern with promise responses, cross-domain function passing, error propagation and timeout handling.

Official sources

  1. Issues
  2. krakenjs/post-robot on GitHub
  3. License: Apache-2.0
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/krakenjs-post-robot.svg)](https://hysenlabs.com/projects/krakenjs-post-robot)