diff --git a/src/promy/promy.js b/src/promy/promy.js
new file mode 100644
index 0000000000000000000000000000000000000000..8e234135f8debf91930fe5c18f595cc7fc6e0a12
--- /dev/null
+++ b/src/promy/promy.js
@@ -0,0 +1,835 @@
+/*
+ * Promy: Promises library
+ * Copyright (C) 2013  Nexedi SA
+ *
+ *   This library is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
+/*global setInterval, setTimeout, clearInterval, clearTimeout */
+
+(function (dependencies, module) {
+  "use strict";
+  /*global define, exports, window */
+  if (typeof define === 'function' && define.amd) {
+    return define(dependencies, module);
+  }
+  if (typeof exports === 'object') {
+    module(exports);
+  }
+  if (typeof window === 'object') {
+    window.promy = {};
+    module(window.promy);
+  }
+}(['exports'], function (exports) {
+  "use strict";
+
+  var UNRESOLVED = 0, RESOLVED = 1, REJECTED = 2, CANCELLED = 3;
+
+  /**
+   * thenItem(item, [onSucess], [onError], [onProgress]): any
+   *
+   * Execute one of the given callback when the item is fulfilled. If the item
+   * is not a promise, then onSuccess is called with the item as first
+   * parameter.
+   *
+   * @param  {Any} item A promise, deferred or a simple value
+   * @param  {Function} [onSuccess] The callback to call on resolve
+   * @param  {Function} [onError] The callback to call on reject
+   * @param  {Function} [onProgress] The callback to call on notify
+   */
+  function thenItem(item, onSuccess, onError, onProgress) {
+    if (typeof item === 'object' && item !== null) {
+      if (typeof item.promise === 'object' && item.promise !== null &&
+          typeof item.promise.then === 'function') {
+        // item seams to be a Deferred
+        return item.promise.then(
+          onSuccess,
+          onError,
+          onProgress
+        );
+      }
+      if (typeof item.then === 'function') {
+        // item seams to be a Promise
+        return item.then(
+          onSuccess,
+          onError,
+          onProgress
+        );
+      }
+    }
+    return onSuccess(item);
+  }
+
+  /**
+   * promiseResolve(promise, answers): any
+   *
+   * Resolve the promise with the given answers.
+   *
+   * @param  {Promise} promise The promise to resolve
+   * @param  {Array} answers The arguments to give
+   */
+  function promiseResolve(promise, answers) {
+    var array;
+    if (promise._state === UNRESOLVED) {
+      promise._state = RESOLVED;
+      promise._answers = answers;
+      array = promise._onResolve.slice();
+      setTimeout(function () {
+        var i;
+        for (i = 0; i < array.length; i += 1) {
+          try {
+            array[i].apply(promise, promise._answers);
+          } catch (ignore) {} // errors will never be retrieved by global
+        }
+      });
+      // free the memory
+      promise._onResolve = undefined;
+      promise._onReject = undefined;
+      promise._onProgress = undefined;
+    }
+  }
+
+  /**
+   * promiseReject(promise, answers): any
+   *
+   * Reject the promise with the given answers.
+   *
+   * @param  {Promise} promise The promise to reject
+   * @param  {Array} answers The arguments to give
+   */
+  function promiseReject(promise, answers) {
+    var array;
+    if (promise._state === UNRESOLVED) {
+      promise._state = REJECTED;
+      promise._answers = answers;
+      array = promise._onReject.slice();
+      setTimeout(function () {
+        var i;
+        for (i = 0; i < array.length; i += 1) {
+          try {
+            array[i].apply(promise, promise._answers);
+          } catch (ignore) {} // errors will never be retrieved by global
+        }
+      });
+      // free the memory
+      promise._onResolve = undefined;
+      promise._onReject = undefined;
+      promise._onProgress = undefined;
+    }
+  }
+
+  /**
+   * promiseNotify(promise, answers): any
+   *
+   * Notify the promise with the given answers.
+   *
+   * @param  {Promise} promise The promise to notify
+   * @param  {Array} answers The arguments to give
+   */
+  function promiseNotify(promise, answers) {
+    var i;
+    if (promise._onProgress) {
+      for (i = 0; i < promise._onProgress.length; i += 1) {
+        try {
+          promise._onProgress[i].apply(promise, answers);
+        } catch (ignore) {} // errors will never be retrieved by global
+      }
+    }
+  }
+
+  /**
+   * Promise(cancel)
+   *
+   * @class Promise
+   * @constructor
+   */
+  function Promise(cancel) {
+    this._onReject = [];
+    this._onResolve = [];
+    this._onProgress = [];
+    this._state = UNRESOLVED;
+    if (typeof cancel === 'function') {
+      this.promise._cancel = cancel;
+    }
+  }
+
+  ////////////////////////////////////////////////////////////
+  // http://wiki.commonjs.org/wiki/Promises/A
+  // then(fulfilledHandler, errorHandler, progressHandler)
+
+  /**
+   * then([onSuccess], [onError], [onProgress]): Promise
+   *
+   * Returns a new Promise with the return value of the `onSuccess` or `onError`
+   * callback as first parameter. If the pervious promise is resolved, the
+   * `onSuccess` callback is called. If rejected, the `onError` callback is
+   * called. If notified, `onProgress` is called.
+   *
+   *     Deferred.when(1).
+   *       then(function (one) { return one + 1; }).
+   *       then(console.log); // shows 2
+   *
+   * @method then
+   * @param  {Function} [onSuccess] The callback to call on resolve
+   * @param  {Function} [onError] The callback to call on reject
+   * @param  {Function} [onProgress] The callback to call on notify
+   * @return {Promise} The new promise
+   */
+  Promise.prototype.then = function (onSuccess, onError, onProgress) {
+    /*global Deferred*/
+    var defer, next = new this.constructor(this._cancel), that = this;
+    defer = new Deferred();
+    defer.promise = next;
+    switch (this._state) {
+    case RESOLVED:
+      if (typeof onSuccess === 'function') {
+        setTimeout(function () {
+          try {
+            thenItem(
+              onSuccess.apply(that, that._answers),
+              defer.resolve.bind(defer),
+              defer.reject.bind(defer)
+            );
+          } catch (e) {
+            defer.reject(e);
+          }
+        });
+      } else {
+        setTimeout(function () {
+          defer.resolve.apply(defer, that._answers);
+        });
+      }
+      break;
+    case REJECTED:
+      if (typeof onError === 'function') {
+        setTimeout(function () {
+          var result;
+          try {
+            result = onError.apply(that, that._answers);
+            if (result === undefined) {
+              return defer.reject.apply(defer, that._answers);
+            }
+            thenItem(
+              result,
+              defer.reject.bind(defer),
+              defer.reject.bind(defer)
+            );
+          } catch (e) {
+            defer.reject(e);
+          }
+        });
+      } else {
+        setTimeout(function () {
+          defer.reject.apply(defer, that._answers);
+        });
+      }
+      break;
+    case UNRESOLVED:
+      if (typeof onSuccess === 'function') {
+        this._onResolve.push(function () {
+          try {
+            thenItem(
+              onSuccess.apply(that, arguments),
+              defer.resolve.bind(defer),
+              defer.reject.bind(defer),
+              defer.notify.bind(defer)
+            );
+          } catch (e) {
+            defer.reject(e);
+          }
+        });
+      } else {
+        this._onResolve.push(function () {
+          defer.resolve.apply(defer, arguments);
+        });
+      }
+      if (typeof onError === 'function') {
+        this._onReject.push(function () {
+          try {
+            thenItem(
+              onError.apply(that, that._answers),
+              defer.reject.bind(defer),
+              defer.reject.bind(defer)
+            );
+          } catch (e) {
+            defer.reject(e);
+          }
+        });
+      } else {
+        this._onReject.push(function () {
+          defer.reject.apply(defer, that._answers);
+        });
+      }
+      if (typeof onProgress === 'function') {
+        this._onProgress.push(function () {
+          var result;
+          try {
+            result = onProgress.apply(that, arguments);
+            if (result === undefined) {
+              defer.notify.apply(defer, arguments);
+            } else {
+              defer.notify(result);
+            }
+          } catch (e) {
+            defer.notify.apply(defer, arguments);
+          }
+        });
+      } else {
+        this._onProgress.push(function () {
+          defer.notify.apply(defer, arguments);
+        });
+      }
+      break;
+    default:
+      break;
+    }
+    return next;
+  };
+
+  // p.resolve() ?
+  // p.reject() ?
+  // p.notify() ?
+
+  /**
+   * p.cancel(): p
+   *
+   * Cancels the operation by calling promise._cancel().
+   *
+   * @method cancel
+   * @return {Promise} this
+   */
+  Promise.prototype.cancel = function () {
+    if (this._state === UNRESOLVED) {
+      this._state = CANCELLED;
+      if (typeof this._cancel === 'function') {
+        this._cancel();
+      }
+      this._onResolve = undefined;
+      this._onReject = undefined;
+      this._onProgress = undefined;
+    }
+    return this;
+  };
+
+  /**
+   * p.timeout(delay): p
+   *
+   * Reject the promise with an Error("Timeout") and cancel the operation.
+   *
+   * @method timeout
+   * @param  {Number} delay The delay before rejection
+   * @return {Promise} this
+   */
+  Promise.prototype.timeout = function (delay) {
+    return exports.choose(this, exports.delay(delay).then(function () {
+      throw new Error("Timeout (" + delay + ")");
+    }));
+  };
+
+  ////////////////////////////////////////////////////////////
+  // http://wiki.commonjs.org/wiki/Promises/A
+  // get(propertyName)
+
+  /**
+   * get(property): Promise
+   *
+   * Get the property of the promise response as first parameter of the new
+   * Promise.
+   *
+   *     Deferred.when({'a': 'b'}).get('a').then(console.log); // shows 'b'
+   *
+   * @method get
+   * @param  {String} property The object property name
+   * @return {Promise} The promise
+   */
+  Promise.prototype.get = function (property) {
+    return this.then(function (dict) {
+      return dict[property];
+    });
+  };
+
+  ////////////////////////////////////////////////////////////
+  // http://wiki.commonjs.org/wiki/Promises/A
+  // call(functionName, arg1, arg2, ...)
+
+  /**
+   * call(function_name, *args): Promise
+   *
+   *     Deferred.when({'a': console.log}).call('a', 'b'); // shows 'b'
+   *
+   * @method call
+   * @param  {String} function_name The function to call
+   * @param  {Any} *[args] The function arguments
+   * @return {Promise} A new promise
+   */
+  Promise.prototype.call = function (function_name) {
+    var args = Array.prototype.slice.call(arguments, 1);
+    return this.then(function (dict) {
+      return dict[function_name].apply(dict, args);
+    });
+  };
+
+  /**
+   * put(property, value): Promise
+   *
+   * Put a property value from a promise response and return the registered
+   * value as first parameter of the new Promise.
+   *
+   *     Deferred.when({'a': 'b'}).put('a', 'c').then(console.log); // shows 'c'
+   *
+   * @method put
+   * @param  {String} property The object property name
+   * @param  {String} value The value to put
+   * @return {Promise} A new promise
+   */
+  Promise.prototype.put = function (property, value) {
+    return this.then(function (dict) {
+      dict[property] = value;
+      return dict[property];
+    });
+  };
+
+  /**
+   * p.done(callback): p
+   *
+   * Call the callback on resolve.
+   *
+   *     Deferred.when(1).
+   *       done(function (one) { return one + 1; }).
+   *       done(console.log); // shows 1
+   *
+   * @method done
+   * @param  {Function} callback The callback to call on resolve
+   * @return {Promise} This promise
+   */
+  Promise.prototype.done = function (callback) {
+    var that = this;
+    if (typeof callback !== 'function') {
+      return this;
+    }
+    switch (this._state) {
+    case RESOLVED:
+      setTimeout(function () {
+        try {
+          callback.apply(that, that._answers);
+        } catch (ignore) {} // errors will never be retrieved by global
+      });
+      break;
+    case UNRESOLVED:
+      this._onResolve.push(callback);
+      break;
+    default:
+      break;
+    }
+    return this;
+  };
+
+  /**
+   * p.fail(callback): p
+   *
+   * Call the callback on reject.
+   *
+   *     promisedTypeError().
+   *       fail(function (e) { name_error(); }).
+   *       fail(console.log); // shows TypeError
+   *
+   * @method fail
+   * @param  {Function} callback The callback to call on reject
+   * @return {Promise} This promise
+   */
+  Promise.prototype.fail = function (callback) {
+    var that = this;
+    if (typeof callback !== 'function') {
+      return this;
+    }
+    switch (this._state) {
+    case REJECTED:
+      setTimeout(function () {
+        try {
+          callback.apply(that, that._answers);
+        } catch (ignore) {} // errors will never be retrieved by global
+      });
+      break;
+    case UNRESOLVED:
+      this._onReject.push(callback);
+      break;
+    default:
+      break;
+    }
+    return this;
+  };
+
+  /**
+   * p.progress(callback): p
+   *
+   * Call the callback on notify.
+   *
+   *     Promise.delay(100, 10).
+   *       progress(function () { return null; }).
+   *       progress(console.log); // does not show null
+   *
+   * @method progress
+   * @param  {Function} callback The callback to call on notify
+   * @return {Promise} This promise
+   */
+  Promise.prototype.progress = function (callback) {
+    if (typeof callback !== 'function') {
+      return this;
+    }
+    switch (this._state) {
+    case UNRESOLVED:
+      this._onProgress.push(callback);
+      break;
+    default:
+      break;
+    }
+    return this;
+  };
+
+  /**
+   * p.always(callback): p
+   *
+   * Call the callback on resolve or on reject.
+   *
+   *     sayHello().
+   *       done(iAnswer).
+   *       fail(iHeardNothing).
+   *       always(iKeepWalkingAnyway);
+   *
+   * @method always
+   * @param  {Function} callback The callback to call on resolve or on reject
+   * @return {Promise} This promise
+   */
+  Promise.prototype.always = function (callback) {
+    var that = this;
+    if (typeof callback !== 'function') {
+      return this;
+    }
+    switch (this._state) {
+    case RESOLVED:
+    case REJECTED:
+      setTimeout(function () {
+        try {
+          callback.apply(that, that._answers);
+        } catch (ignore) {} // errors will never be retrieved by global
+      });
+      break;
+    case UNRESOLVED:
+      that._onReject.push(callback);
+      that._onResolve.push(callback);
+      break;
+    default:
+      break;
+    }
+    return this;
+  };
+
+  exports.Promise = Promise;
+
+  /**
+   * Deferred(cancel)
+   *
+   * @class Deferred
+   * @constructor
+   */
+  function Deferred(cancel) {
+    this.promise = new Promise(cancel);
+  }
+
+  /**
+   * resolve(*args): any
+   *
+   * Resolves the promise with the given arguments.
+   *
+   * @method resolve
+   * @param  {Any} *[args] The arguments to give
+   */
+  Deferred.prototype.resolve = function () {
+    return promiseResolve(this.promise, arguments);
+  };
+
+  /**
+   * reject(*args): any
+   *
+   * Rejects the promise with the given arguments.
+   *
+   * @method reject
+   * @param  {Any} *[args] The arguments to give
+   */
+  Deferred.prototype.reject = function () {
+    return promiseReject(this.promise, arguments);
+  };
+
+  /**
+   * notify(*args): any
+   *
+   * Notifies the promise with the given arguments.
+   *
+   * @method notify
+   * @param  {Any} *[args] The arguments to give
+   */
+  Deferred.prototype.notify = function () {
+    return promiseNotify(this.promise, arguments);
+  };
+
+  exports.Deferred = Deferred;
+
+  //////////////////////////////////////////////////////////////////////
+  // Inspired by Task.js
+
+  /**
+   * now(value): Promise
+   *
+   * Converts an ordinary value into a fulfilled promise.
+   *
+   * @param  {Any} value The value to use
+   * @return {Promise} The resolved promise
+   */
+  exports.now = function (value) {
+    var deferred = new Deferred();
+    deferred.resolve(value);
+    return deferred.promise;
+  };
+
+  /**
+   * join(*promises): Promise
+   *
+   * Produces a promise that is resolved when all the given promises are
+   * resolved. The resolved value is an array of each of the resolved values of
+   * the given promises.
+   *
+   * If any of the promises is rejected, the joined promise is rejected with the
+   * same error, and any remaining unfulfilled promises are cancelled.
+   *
+   * @param  {Promise} *[promises] The promises to join
+   * @return {Promise} A new promise
+   */
+  exports.join = function () {
+    var promises, results = [], i, count = 0, deferred;
+    promises = Array.prototype.slice.call(arguments);
+    function cancel() {
+      var j;
+      for (j = 0; j < promises.length; j += 1) {
+        promises[j].cancel();
+      }
+    }
+    deferred = new Deferred(cancel);
+    function succeed(j) {
+      return function (answer) {
+        results[j] = answer;
+        count += 1;
+        if (count !== promises.length) {
+          return;
+        }
+        deferred.resolve(results);
+      };
+    }
+    function failed(answer) {
+      cancel();
+      deferred.reject(answer);
+    }
+    function notify(j) {
+      return function (answer) {
+        deferred.notify({
+          "promise": this,
+          "index": j,
+          "answer": answer
+        });
+      };
+    }
+    for (i = 0; i < promises.length; i += 1) {
+      promises[i].then(succeed(i), failed, notify(i));
+    }
+    return deferred.promise;
+  };
+
+  /**
+   * choose(*promises): Promise
+   *
+   * Produces a promise that is fulfilled when any one of the given promises is
+   * fulfilled. As soon as one of the promises is fulfilled, whether by being
+   * resolved or rejected, all the other promises are cancelled.
+   *
+   * @param  {Promise} *[promises] The promises to use
+   * @return {Promise} A new promise
+   */
+  exports.choose = function () {
+    var promises, i, deferred;
+    promises = Array.prototype.slice.call(arguments);
+    function cancel() {
+      var j;
+      for (j = 0; j < promises.length; j += 1) {
+        promises[j].cancel();
+      }
+    }
+    deferred = new Deferred(cancel);
+    function succeed(answer) {
+      cancel();
+      deferred.resolve(answer);
+    }
+    function failed(answer) {
+      cancel();
+      deferred.reject(answer);
+    }
+    function notify(j) {
+      return function (answer) {
+        deferred.notify({
+          "promise": this,
+          "index": j,
+          "answer": answer
+        });
+      };
+    }
+    for (i = 0; i < promises.length; i += 1) {
+      promises[i].then(succeed, failed, notify(i));
+    }
+    return deferred.promise;
+  };
+
+  /**
+   * sleep(delay[, every]): Promise
+   *
+   * Resolve the promise after `timeout` milliseconds and notfies us every
+   * `every` milliseconds.
+   *
+   *     Deferred.delay(50, 10).then(console.log, console.error, console.log);
+   *     // // shows
+   *     // 10 // from progress
+   *     // 20 // from progress
+   *     // 30 // from progress
+   *     // 40 // from progress
+   *     // 50 // from progress
+   *     // 50 // from success
+   *
+   * @param  {Number} delay In milliseconds
+   * @param  {Number} [every] In milliseconds
+   * @return {Promise} A new promise
+   */
+  exports.sleep = function (delay, every) {
+    var deferred, timeout, interval, now = 0;
+    function cancel() {
+      clearTimeout(timeout);
+      clearInterval(interval);
+    }
+    deferred = new Deferred(cancel);
+    if (typeof every === 'number' && isFinite(every)) {
+      interval = setInterval(function () {
+        now += every;
+        deferred.notify(now);
+      }, every);
+    }
+    timeout = setTimeout(function () {
+      clearInterval(interval);
+      deferred.notify(delay);
+      deferred.resolve(delay);
+    }, delay);
+    return deferred.promise;
+  };
+
+  ////////////////////////////////////////////////////////////
+  // http://wiki.commonjs.org/wiki/Promises/B
+  // when(value, callback, errback_opt)
+
+  /**
+   * when(item, [onSuccess], [onError], [onProgress]): Promise
+   *
+   * Return an item as first parameter of the promise answer. If item is of
+   * type Promise, the method will just return the promise. If item is of type
+   * Deferred, the method will return the deferred promise.
+   *
+   *     Deferred.when('a').then(console.log); // shows 'a'
+   *
+   * @param  {Any} item The item to use
+   * @param  {Function} [onSuccess] The callback called on success
+   * @param  {Function} [onError] the callback called on error
+   * @param  {Function} [onProgress] the callback called on progress
+   * @return {Promise} The promise
+   */
+  exports.when = function (item, onSuccess, onError, onProgress) {
+    if (typeof item === 'object' && item !== null) {
+      if (typeof item.promise === 'object' && item.promise !== null &&
+          typeof item.promise.then === 'function') {
+        // item seams to be a Deferred
+        return item.promise.then(onSuccess, onError, onProgress);
+      }
+      if (typeof item.then === 'function') {
+        // item seams to be a Promise
+        return item.then(onSuccess, onError, onProgress);
+      }
+    }
+    // item is just a value, convert into fulfilled promise
+    var deferred = new Deferred(), promise;
+    if (typeof onSuccess === 'function') {
+      promise = deferred.promise.then(onSuccess);
+    } else {
+      promise = deferred.promise;
+    }
+    deferred.resolve(item);
+    return promise;
+  };
+
+  ////////////////////////////////////////////////////////////
+  // http://wiki.commonjs.org/wiki/Promises/B
+  // get(object, name)
+
+  /**
+   * get(dict, property): Promise
+   *
+   * Return the dict property as first parameter of the promise answer.
+   *
+   *     Deferred.get({'a': 'b'}, 'a').then(console.log); // shows 'b'
+   *
+   * @param  {Object} dict The object to use
+   * @param  {String} property The object property name
+   * @return {Promise} The promise
+   */
+  exports.get = function (dict, property) {
+    var p = new Deferred();
+    try {
+      p.resolve(dict[property]);
+    } catch (e) {
+      p.reject(e);
+    }
+    return p;
+  };
+
+  ////////////////////////////////////////////////////////////
+  // http://wiki.commonjs.org/wiki/Promises/B
+  // put(object, name, value)
+
+  /**
+   * put(dict, property, value): Promise
+   *
+   * Set and return the dict property as first parameter of the promise answer.
+   *
+   *     Deferred.put({'a': 'b'}, 'a', 'c').then(console.log); // shows 'c'
+   *
+   * @param  {Object} dict The object to use
+   * @param  {String} property The object property name
+   * @param  {Any} value The value
+   * @return {Promise} The promise
+   */
+  exports.put = function (dict, property, value) {
+    var p = new Deferred();
+    try {
+      dict[property] = value;
+      p.resolve(dict[property]);
+    } catch (e) {
+      p.reject(e);
+    }
+    return p;
+  };
+
+}));