diff --git a/README.md b/README.md
index e69de29..38c282c 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,110 @@
+privacyidea-checkotp
+====================
+
+Shell script implementing the [PrivacyIDEA](http://www.privacyidea.org) OTP (One
+Time Password) check to integrate with [FreeRadius](http://www.freeradius.org)
+in environments where the FreeRadius Perl plugin is not available to use the
+standard check script (e.g. on OS X 10.9).
+
+**Version 1.0**, latest version, documentation and bugtracker available on my
+[GitLab instance](https://gitlab.lindenaar.net/scripts/privacyidea-checkotp)
+
+Copyright (c) 2015 Frederik Lindenaar. free for distribution under the GNU
+License, see [below](#license)
+
+
+Introduction
+------------
+When integrating PrivacyIDEA with the stock OS X Server FreeRadius server, I was
+blocked by the installation not including the `rlm_perl` module. This bash
+(shell) script was created to get around that as it is to be executed using the
+FreeRadius `rlm_exec` module. Please bear in mind that this module suits my
+needs and probably still has a few glitches, though it turned out to be a stable
+solution for my needs. In case you have any comments / questions or issues,
+please raise them through my [GitLab instance](https://gitlab.lindenaar.net/scripts/privacyidea-checkotp) so that all users benefit.
+
+Setup
+-----
+This script will be executed using the FreeRadius `rtl_exec` module, which is
+not the most efficient way to integrate but will suffice for low to medium
+volume use. The script depends on `curl` and `sed` being installed, which is
+the case in most environments.
+
+The setup of this solution consists of the following steps:
+
+  1. Setup PrivacyIDEA and make sure it is working on its own
+  2. Install the `privacyidea-checkotp` on your FreeRadius server and make it
+     executable
+  3. Copy the provided `privacyidea.freeradiusmodule` into the FreeRadius
+     `raddb/modules` directory as `privacyidea`
+  4. Update `raddb/modules/privacyidea` so that `[WRAPPERSCRIPT_PATH]` points to
+     the script as installed in step #1 and `[PRIVACYIDEA_URL]` is replaced with
+     the base URL of your PrivacyIDEA instance.
+  5. Check your configuration by running the command configured in
+     `raddb/modules/privacyidea` followed by a username and valid
+     password/OTP/PIN combination (depending on your configuration. To avoid the
+     password being captured in your shell history, use `` `cat` `` instead of
+     the password on the commandline and after entering the command, enter the
+     password/OTP/PIN combination as PrivacyIDEA expects followed by an enter
+     and `CTRL-D`.
+  6. After successfully testing the base setup, add PrivacyIDEA as authorization
+     and authentication provider with the following steps:
+     1. Open the virtual host file you want to add PrivacyIDEA authentication to
+     (typically in `raddb/sites-available`)
+     2. In the section `authorize {`:
+        * disable all authorization modules you do not want to succeed
+        * add the following to the bottom of this section:
+
+          ~~~
+          # Use PrivacyIDEA
+          if(! Service-Type == "Outbound-User") {
+                update control {
+                        Auth-Type := PrivacyIDEA
+                }
+          }
+          else {
+                # Service-Type == "Outbound-User"
+                if(NAS-Port-Type == "Virtual" && NAS-Port > 0 ) {
+                        update control {
+                                Auth-Type := Accept
+                        }
+                }
+          }
+          ~~~
+
+     3. In the section `authenticate {`:
+        * Disable all authentication modules you do not want to succeed
+        * add the following to the top of this section so that PrivacyIDEA
+          authentication is tried first:
+
+          ~~~
+          Auth-Type PrivacyIDEA {
+                privacyidea
+          }
+          ~~~
+
+  7. Last step is to test the configuration, run FreeRadius as `radiusd -X` and
+     check what happens with an authentication requests reaching the FreeRadius
+     server. Specifc requirements on what needs to happen is dependant on your
+     setup (e.g. I am normally not using any PIN codes for the OTP, but require
+     the user's password followed by the OTP).
+
+Please note that this setups works for plain-text (i.e. non-EAP) authentication
+with FreeRadius, which is what my setup needs. The configuration above does not
+work with EAP authentication, I am still working on that (any hints for that are
+welcome!)
+
+<a name="license">License</a>
+-----------------------------
+This script, documentation and configration examples are free software: you can
+redistribute and/or modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, either version 3 of the License,
+or (at your option) any later version.
+
+This script, documenatation and configuration examples are 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
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program.  If not, download it from <http://www.gnu.org/licenses/>.
diff --git a/privacyidea.freeradiusmodule b/privacyidea.freeradiusmodule
new file mode 100644
index 0000000..64f3fdf
--- /dev/null
+++ b/privacyidea.freeradiusmodule
@@ -0,0 +1,105 @@
+#
+# Sample FreeRadius rlm_exec wrapper configuration to perform OTP authentication
+# against privacyidea without rtl_perl module (unavailable on OS X 10.9 Server)
+#
+# Version 1.0, latest version, documentation and bugtracker available at:
+#               https://gitlab.lindenaar.net/scripts/privacyidea-checkotp
+#
+# Copyleft (c) 2015 by Frederik Lindenaar
+#
+
+#
+# Return value of the program run determines the result of the exec instance
+# call (See doc/configurable_failover for details) as follows: 
+#
+#  < 0 : fail      the module failed
+#  = 0 : ok        the module succeeded
+#  = 1 : reject    the module rejected the user
+#  = 2 : fail      the module failed
+#  = 3 : ok        the module succeeded
+#  = 4 : handled   the module has done everything to handle the request
+#  = 5 : invalid   the user's configuration entry was invalid
+#  = 6 : userlock  the user was locked out
+#  = 7 : notfound  the user was not found
+#  = 8 : noop      the module did nothing
+#  = 9 : updated   the module updated information in the request
+#  > 9 : fail      the module failed
+#
+
+exec privacyidea {
+	#
+	#  Wait for the program to finish.
+	#
+	#  If we do NOT wait, then the program is "fire and
+	#  forget", and any output attributes from it are ignored.
+	#
+	#  If we are looking for the program to output
+	#  attributes, and want to add those attributes to the
+	#  request, then we MUST wait for the program to
+	#  finish, and therefore set 'wait=yes'
+	#
+	# allowed values: {no, yes}
+	wait = yes
+
+	#
+	#  The name of the program to execute, and it's
+	#  arguments.  Dynamic translation is done on this
+	#  field, so things like the following example will
+	#  work.
+	#
+	program = "[WRAPPERSCRIPT_PATH]/privacyidea-checkotp [PRIVACYIDEA_URL]"
+
+	#
+	#  The attributes which are placed into the
+	#  environment variables for the program.
+	#
+	#  Allowed values are:
+	#
+	#	request		attributes from the request
+	#	config		attributes from the configuration items list
+	#	reply		attributes from the reply
+	#	proxy-request	attributes from the proxy request
+	#	proxy-reply	attributes from the proxy reply
+	#
+	#  Note that some attributes may not exist at some
+	#  stages.  e.g. There may be no proxy-reply
+	#  attributes if this module is used in the
+	#  'authorize' section.
+	#
+	input_pairs = request
+
+	#
+	#  Where to place the output attributes (if any) from
+	#  the executed program.  The values allowed, and the
+	#  restrictions as to availability, are the same as
+	#  for the input_pairs.
+	#
+	output_pairs = 
+
+	#
+	#  When to execute the program.  If the packet
+	#  type does NOT match what's listed here, then
+	#  the module does NOT execute the program.
+	#
+	#  For a list of allowed packet types, see
+	#  the 'dictionary' file, and look for VALUEs
+	#  of the Packet-Type attribute.
+	#
+	#  By default, the module executes on ANY packet.
+	#  Un-comment out the following line to tell the
+	#  module to execute only if an Access-Accept is
+	#  being sent to the NAS.
+	#
+	packet_type = Access-Request
+
+	#
+	#  Should we escape the environment variables?
+	#  
+	#  If this is set, all the RADIUS attributes
+	#  are capitalised and dashes replaced with
+	#  underscores. Also, RADIUS values are surrounded
+	#  with double-quotes.
+	#
+	#  That is to say: User-Name=BobUser => USER_NAME="BobUser"
+	shell_escape = yes
+}