diff --git a/README.md b/README.md
new file mode 100644
index 0000000..946f32f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,58 @@
+# ia-headers
+
+This module converts an object containing metadata into valid HTTP headers for the [Internet Archive](https://archive.org/) S3 API.
+
+This is not a full-blown API client - it simply generates the metadata headers. You can use a module like [bhttp](https://www.npmjs.com/package/bhttp) to interact with the API. The documentation for the Internet Archive S3 API can be found [here](https://archive.org/help/abouts3.txt). API keys can be obtained [here](http://archive.org/account/s3.php).
+
+`ia-header` supports multiple values for a metadata field - simply pass them in as an array (see also the Usage example below). Underscores are translated into double dashes, as per the IA S3 documentation.
+
+## License
+
+[WTFPL](http://www.wtfpl.net/txt/copying/) or [CC0](https://creativecommons.org/publicdomain/zero/1.0/), whichever you prefer. A donation and/or attribution are appreciated, but not required.
+
+## Donate
+
+My income consists entirely of donations for my projects. If this module is useful to you, consider [making a donation](http://cryto.net/~joepie91/donate.html)!
+
+You can donate using Bitcoin, PayPal, Gratipay, Flattr, cash-in-mail, SEPA transfers, and pretty much anything else.
+
+## Contributing
+
+Pull requests welcome. Please make sure your modifications are in line with the overall code style, and ensure that you're editing the `.coffee` files, not the `.js` files.
+
+Build tool of choice is `gulp`; simply run `gulp` while developing, and it will watch for changes.
+
+Be aware that by making a pull request, you agree to release your modifications under the licenses stated above.
+
+## Caveats
+
+* Metadata keys (not values!) may only be ASCII. The Internet Archive does not currently support non-ASCII metadata keys.
+* All non-ASCII and non-printable characters in metadata values are URL-encoded - this could cause potentially significant header size increases. In most cases, this won't matter.
+
+## Usage
+
+```javascript
+var iaHeaders = require("ia-headers");
+
+var metadata = {
+ subject: ["mirror", "pdf.yt"],
+ mediatype: "texts",
+ collection: "pdfymirrors",
+ date: "2014-03-01",
+ title: "Some Document (PDFy mirror)",
+ description: "This is a document from PDFy.
It's a mirror."
+}
+
+var metadataHeaders = iaHeaders(metadata);
+console.log(metadataHeaders);
+
+/* Result:
+{ 'x-archive-meta1-subject': 'mirror',
+ 'x-archive-meta2-subject': 'pdf.yt',
+ 'x-archive-meta-mediatype': 'texts',
+ 'x-archive-meta-collection': 'pdfymirrors',
+ 'x-archive-meta-date': '2014-03-01',
+ 'x-archive-meta-title': 'Some Document (PDFy mirror)',
+ 'x-archive-meta-description': 'This is a document from PDFy.
It\'s a mirror.' }
+*/
+```
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..bb7f05f
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,28 @@
+var gulp = require('gulp');
+
+/* CoffeeScript compile deps */
+var path = require('path');
+var gutil = require('gulp-util');
+var concat = require('gulp-concat');
+var rename = require('gulp-rename');
+var coffee = require('gulp-coffee');
+var cache = require('gulp-cached');
+var remember = require('gulp-remember');
+var plumber = require('gulp-plumber');
+
+var source = ["lib/**/*.coffee", "index.coffee"]
+
+gulp.task('coffee', function() {
+ return gulp.src(source, {base: "."})
+ .pipe(plumber())
+ .pipe(cache("coffee"))
+ .pipe(coffee({bare: true}).on('error', gutil.log)).on('data', gutil.log)
+ .pipe(remember("coffee"))
+ .pipe(gulp.dest("."));
+});
+
+gulp.task('watch', function () {
+ gulp.watch(source, ['coffee']);
+});
+
+gulp.task('default', ['coffee', 'watch']);
\ No newline at end of file
diff --git a/index.coffee b/index.coffee
new file mode 100644
index 0000000..c6097c8
--- /dev/null
+++ b/index.coffee
@@ -0,0 +1 @@
+module.exports = require "./lib"
\ No newline at end of file
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..bf63049
--- /dev/null
+++ b/index.js
@@ -0,0 +1 @@
+module.exports = require("./lib");
diff --git a/lib/index.coffee b/lib/index.coffee
new file mode 100644
index 0000000..f82ec5c
--- /dev/null
+++ b/lib/index.coffee
@@ -0,0 +1,34 @@
+zeroFill = require "zero-fill"
+
+maybeEncodeValue = (value) ->
+ if /[^ -~]/.exec(value) # Matches any Unicode and non-printable characters.
+ encoded = encodeURIComponent(value)
+ return "uri(#{encoded})"
+ else
+ return value
+
+module.exports = (metadata) ->
+ headers = {}
+
+ for key, values of metadata
+ # It's easier to just always operate on an array, even if there's only a single value.
+ if not Array.isArray values
+ values = [values]
+
+ # Per RFC 822, we cannot have underscores in the header names. The IA S3 API will automatically convert double hyphens to an underscore, so we do the inverse.
+ key = key.replace("_", "--")
+
+ itemCount = values.length
+
+ if itemCount == 0
+ continue
+ else if itemCount == 1
+ headers["x-archive-meta-#{key}"] = maybeEncodeValue(values[0])
+ else
+ numberWidth = itemCount.toString().length
+
+ for value, i in values
+ headerName = "x-archive-meta#{zeroFill(numberWidth, i + 1)}-#{key}"
+ headers[headerName] = maybeEncodeValue(value)
+
+ return headers
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..9f071f8
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,39 @@
+var maybeEncodeValue, zeroFill;
+
+zeroFill = require("zero-fill");
+
+maybeEncodeValue = function(value) {
+ var encoded;
+ if (/[^ -~]/.exec(value)) {
+ encoded = encodeURIComponent(value);
+ return "uri(" + encoded + ")";
+ } else {
+ return value;
+ }
+};
+
+module.exports = function(metadata) {
+ var headerName, headers, i, itemCount, key, numberWidth, value, values, _i, _len;
+ headers = {};
+ for (key in metadata) {
+ values = metadata[key];
+ if (!Array.isArray(values)) {
+ values = [values];
+ }
+ key = key.replace("_", "--");
+ itemCount = values.length;
+ if (itemCount === 0) {
+ continue;
+ } else if (itemCount === 1) {
+ headers["x-archive-meta-" + key] = maybeEncodeValue(values[0]);
+ } else {
+ numberWidth = itemCount.toString().length;
+ for (i = _i = 0, _len = values.length; _i < _len; i = ++_i) {
+ value = values[i];
+ headerName = "x-archive-meta" + (zeroFill(numberWidth, i + 1)) + "-" + key;
+ headers[headerName] = maybeEncodeValue(value);
+ }
+ }
+ }
+ return headers;
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..93238c3
--- /dev/null
+++ b/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "ia-headers",
+ "version": "1.0.0",
+ "description": "Takes a metadata object, and turns it into valid HTTP headers for the Internet Archive S3 API.",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git@git.cryto.net:projects/joepie91/node-ia-headers"
+ },
+ "keywords": [
+ "internet",
+ "archive",
+ "s3",
+ "api",
+ "http",
+ "metadata"
+ ],
+ "author": "Sven Slootweg",
+ "license": "WTFPL",
+ "dependencies": {
+ "zero-fill": "^2.2.0"
+ },
+ "devDependencies": {
+ "gulp": "~3.8.0",
+ "gulp-cached": "~0.0.3",
+ "gulp-coffee": "~2.0.1",
+ "gulp-concat": "~2.2.0",
+ "gulp-livereload": "~2.1.0",
+ "gulp-nodemon": "~1.0.4",
+ "gulp-plumber": "~0.6.3",
+ "gulp-remember": "~0.2.0",
+ "gulp-rename": "~1.2.0",
+ "gulp-util": "~2.2.17"
+ }
+}