diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..639e088
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+gem 'csvlint', github: 'ONS-OpenData/csvlint.rb'
+gem 'sinatra'
\ No newline at end of file
diff --git a/app.rb b/app.rb
new file mode 100644
index 0000000..bae6777
--- /dev/null
+++ b/app.rb
@@ -0,0 +1,20 @@
+require 'rubygems'
+require 'bundler/setup'
+require 'sinatra'
+require 'csvlint'
+
+get '/' do
+  erb :index
+end
+
+post '/validate' do
+  tidy_file = params[:tidyDataFile][:tempfile]
+  schema_file = params[:schemaFile][:tempfile]
+  schema_string = schema_file.read
+  csvw_schema = Csvlint::Schema.load_from_string(params[:schemaFile][:filename],
+                                                 schema_string,
+                                                 true)
+  @validator = Csvlint::Validator.new(tidy_file, {}, csvw_schema)
+#  @validator.validate
+  erb :results
+end
diff --git a/public/cogs.svg b/public/cogs.svg
new file mode 100644
index 0000000..e612f72
--- /dev/null
+++ b/public/cogs.svg
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="45.733753mm"
+   height="24.143904mm"
+   viewBox="0 0 45.733753 24.143904"
+   version="1.1"
+   id="svg839"
+   inkscape:version="0.92.4 (unknown)"
+   sodipodi:docname="cogs2.svg">
+  <defs
+     id="defs833">
+    <filter
+       inkscape:collect="always"
+       style="color-interpolation-filters:sRGB"
+       id="filter4601"
+       x="-0.0022188539"
+       width="1.0044377"
+       y="-0.0026133528"
+       height="1.0052267">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.113305"
+         id="feGaussianBlur4603" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       style="color-interpolation-filters:sRGB"
+       id="filter1585"
+       x="-0.010079781"
+       width="1.0201596"
+       y="-0.014824004"
+       height="1.0296479">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.54740723"
+         id="feGaussianBlur1587" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.979899"
+     inkscape:cx="17.246459"
+     inkscape:cy="29.547989"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1048"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <metadata
+     id="metadata836">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-71.891885,-132.50734)">
+    <path
+       style="fill:#eaf2ff;fill-opacity:1;stroke:none;stroke-width:3.77952766;stroke-opacity:1;filter:url(#filter1585)"
+       d="m 371.18555,502.12891 c -2.16255,1.07396 -3.89582,2.1102 -4.11914,3.02148 -0.6711,2.73838 9.85033,12.54175 8.91406,15.20117 -0.93627,2.65943 -15.27827,3.70883 -16.4707,6.26367 -1.19244,2.55485 7.21445,14.22083 5.77734,16.64649 -1.43711,2.42566 -15.70901,0.65656 -17.37695,2.92969 -1.66795,2.27312 4.30264,15.35638 2.41992,17.45507 -1.88272,2.09866 -15.53392,-2.42159 -17.61328,-0.51757 -2.07936,1.90405 1.22277,15.90076 -1.03321,17.59179 -2.25597,1.69108 -14.76492,-5.40516 -17.17578,-3.94336 -1.79568,1.08878 -1.97296,9.33357 -2.99609,13.97657 h 27.08594 c 3.915,-1.81076 8.25399,-3.64449 8.62304,-5.15039 0.6711,-2.73839 -9.85033,-12.54175 -8.91406,-15.20118 0.93627,-2.65939 15.27827,-3.70886 16.4707,-6.26367 1.19244,-2.55485 -7.2164,-14.22082 -5.77929,-16.64648 1.43711,-2.42567 15.709,-0.65657 17.37695,-2.92969 1.66794,-2.27312 -4.30069,-15.35638 -2.41797,-17.45508 1.88272,-2.09869 15.53394,2.42163 17.61328,0.51758 2.07935,-1.90405 -1.22279,-15.90072 1.03321,-17.5918 2.25596,-1.69103 14.76298,5.40513 17.17382,3.94336 1.54505,-0.93683 1.89271,-7.16681 2.61133,-11.84765 z m 43.83203,0 c 2.30416,1.35274 4.3288,2.35924 5.2793,2.01367 0.53758,-0.19545 1.06056,-0.95228 1.57617,-2.01367 z m -120.43946,84.41797 c -0.22589,-0.0156 -0.42423,0.004 -0.58984,0.0644 -0.84442,0.307 -1.65384,2.01331 -2.45312,4.14258 h 11.25586 c -3.18149,-1.95075 -6.50031,-4.08861 -8.2129,-4.20703 z"
+       id="rect4567"
+       inkscape:connector-curvature="0"
+       transform="matrix(0.26458333,0,0,0.26458333,-4.7625002,0)" />
+    <text
+       id="text4541"
+       y="149.80859"
+       x="70.943619"
+       style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:6.61458349px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#1e457e;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       xml:space="preserve"><tspan
+         style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-size:16.93333244px;font-family:'Latin Modern Roman Demi';-inkscape-font-specification:'Latin Modern Roman Demi, Semi-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#1e457e;fill-opacity:1;stroke-width:0.26458332px"
+         y="149.80859"
+         x="70.943619"
+         id="tspan4539"
+         sodipodi:role="line">COGS</tspan></text>
+  </g>
+</svg>
diff --git a/views/index.erb b/views/index.erb
new file mode 100644
index 0000000..0ee1f11
--- /dev/null
+++ b/views/index.erb
@@ -0,0 +1,45 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
+    <title>CSVW Validation</title>
+  </head>
+  <body>
+    <nav class="navbar navbar-light bg-light">
+      <a class="navbar-brand" href="/">
+        <img src="cogs.svg" alt="COGS" width="72"/>
+      </a>
+    </nav>
+    <main role="main">
+      <div class="jumbotron">
+        <div class="container">
+          <h1 class="display-4">CSVW Validation</h1>
+          <p class="lead">Check your CSV file and CSV Schema</p>
+        </div>
+      </div>
+      <div class="container">
+        <form action="validate" method="post" enctype="multipart/form-data">
+          <div class="form-group">
+            <label for="tidyDataFile">Tidy Data CSV</label>
+            <input type="file" class="form-control-file" name="tidyDataFile" id="tidyDataFile">
+          </div>
+          <div class="form-group">
+            <label for="schemaFile">CSVW JSON schema</label>
+            <input type="file" class="form-control-file" name="schemaFile" id="schemaFile">
+          </div>
+          <button type="submit" class="btn btn-primary">Validate</button>
+        </form>
+      </div>
+    </main>
+    <footer class="footer mt-auto py-3">
+      <div class="container">
+        <span class="text-muted">© Office for National Statistics 2019</span>
+      </div>
+    </footer>
+    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
+    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
+  </body>
+</html>
diff --git a/views/results.erb b/views/results.erb
new file mode 100644
index 0000000..fa5ca7a
--- /dev/null
+++ b/views/results.erb
@@ -0,0 +1,31 @@
+<html>
+  <head>
+    <title>Validation Results</title>
+  </head>
+  <body>
+    <dl>
+      <dt>Valid</dt><dd><%= @validator.valid? %></dd>
+      <dt>Errors</dt><dd>
+        <ol>
+          <% @validator.errors do |error| %>
+          <li><%= error %>
+          <% end %>
+        </ol>
+      </dd>
+      <dt>Warnings</dt><dd>
+        <ol>
+          <% @validator.warnings do |warning| %>
+          <li><%= warning %>
+          <% end %>
+        </ol>
+      </dd>
+      <dt>Info</dt><dd>
+        <ol>
+          <% @validator.info_messages do |info| %>
+          <li><%= info %>
+          <% end %>
+        </ol>
+      </dd>
+    </dl>
+  </body>
+</html>