diff --git a/api/package.json b/api/package.json
index 819a5c5..75cb08c 100644
--- a/api/package.json
+++ b/api/package.json
@@ -6,6 +6,7 @@
     "dependencies": {
         "body-parser": "^1.19.0",
         "express": "^4.17.1",
+        "express-validator": "^6.10.0",
         "is-docker": "^2.1.1",
         "js-yaml": "^4.0.0",
         "logplease": "^1.2.15",
diff --git a/api/src/api/v1.js b/api/src/api/v1.js
deleted file mode 100644
index 72db74e..0000000
--- a/api/src/api/v1.js
+++ /dev/null
@@ -1,184 +0,0 @@
-const express = require('express');
-const router  = express.Router();
-
-const runtime = require('../runtime');
-const {Job} = require("../job");
-const package = require('../package')
-const logger = require('logplease').create('api/v1');
-
-router.post('/execute', async function(req, res){
-        const {language, version, files, stdin, args, run_timeout, compile_timeout} = req.body;
-
-        if(!language || typeof language !== "string")
-        {
-            return res
-                .status(400)
-                .send({
-                    message: "language is required as a string"
-                });
-        }
-
-        if(!version || typeof version !== "string")
-        {
-            return res
-                .status(400)
-                .send({
-                    message: "version is required as a string"
-                });
-        }
-
-        if(!files || !Array.isArray(files))
-        {
-            return res
-                .status(400)
-                .send({
-                    message: "files is required as an array"
-                });
-        }
-
-        for (const [i,file] of files.entries()) {
-            if(!file.content || typeof file.content !== "string"){
-                return res
-                    .status(400)
-                    .send({
-                        message: `files[${i}].content is required as a string`
-                    });
-            }
-        }
-
-
-
-    
-        const rt = runtime.get_latest_runtime_matching_language_version(language, version);
-
-        if (rt === undefined) {
-            return res
-                .status(400)
-                .send({
-                    message: `${language}-${version} runtime is unknown`
-                });
-        }
-
-        const job = new Job({
-            runtime: rt,
-            alias: language,
-            files: files,
-            args: args || [],
-            stdin: stdin || "",
-            timeouts: {
-                run: run_timeout || 3000,
-                compile: compile_timeout || 10000
-            }
-        });
-
-        await job.prime();
-
-        const result = await job.execute();
-
-        await job.cleanup();
-
-        return res
-            .status(200)
-            .send(result);
-});
-
-router.get('/runtimes', function(req, res){
-    const runtimes = runtime.map(rt => ({
-        language: rt.language,
-        version: rt.version.raw,
-        aliases: rt.aliases,
-        runtime: rt.runtime
-    }));
-
-    return res
-        .status(200)
-        .send(runtimes);
-});
-
-router.get('/packages', async function(req, res){
-    logger.debug('Request to list packages');
-    let packages = await package.get_package_list();
-
-    packages = packages
-        .map(pkg => {
-                return {
-                    language: pkg.language,
-                    language_version: pkg.version.raw,
-                    installed: pkg.installed
-                };
-            });
-
-    return res
-        .status(200)
-        .send(packages);
-});
-
-router.post('/packages/:language/:version', async function(req, res){
-    logger.debug('Request to install package');
-
-    const {language, version} = req.params;
-
-    const pkg = await package.get_package(language, version);
-
-    if (pkg == null) {
-        return res
-            .status(404)
-            .send({
-                message: `Requested package ${language}-${version} does not exist`
-            });
-    }
-
-    try {
-        const response = await pkg.install();
-
-        return res
-            .status(200)
-            .send(response);
-    } catch(e) {
-        logger.error(`Error while installing package ${pkg.language}-${pkg.version}:`, e.message);
-
-        return res
-            .status(500)
-            .send({
-                message: e.message
-            });
-    }
-});
-
-router.delete('/packages/:language/:version', async function(req, res){
-    logger.debug('Request to uninstall package');
-
-    const {language, version} = req.params;
-
-    const pkg = await package.get_package(language, version);
-
-    if (pkg == null) {
-        return res
-            .status(404)
-            .send({
-                message: `Requested package ${language}-${version} does not exist`
-            });
-    }
-
-    try {
-        const response = await pkg.uninstall();
-
-        return res
-            .status(200)
-            .send(response);
-    } catch(e) {
-        logger.error(`Error while uninstalling package ${pkg.language}-${pkg.version}:`, e.message);
-
-        return res
-            .status(500)
-            .send({
-                message: e.message
-            });
-    }
-});
-
-
-
-
-
-module.exports = router;
diff --git a/api/src/job.js b/api/src/executor/job.js
similarity index 97%
rename from api/src/job.js
rename to api/src/executor/job.js
index d68ad98..63122d0 100644
--- a/api/src/job.js
+++ b/api/src/executor/job.js
@@ -1,9 +1,9 @@
-const logger = require('logplease').create('job');
+const logger = require('logplease').create('executor/job');
 const {v4: uuidv4} = require('uuid');
 const cp = require('child_process');
 const path = require('path');
-const config = require('./config');
-const globals = require('./globals');
+const config = require('../config');
+const globals = require('../globals');
 const fs = require('fs/promises');
 
 const job_states = {
diff --git a/api/src/executor/routes.js b/api/src/executor/routes.js
new file mode 100644
index 0000000..d7cabff
--- /dev/null
+++ b/api/src/executor/routes.js
@@ -0,0 +1,57 @@
+// {"language":"python","version":"3.9.1","files":{"code.py":"print('hello world')"},"args":[],"stdin":"","compile_timeout":10, "run_timeout":3}
+// {"success":true, "run":{"stdout":"hello world", "stderr":"", "error_code":0},"compile":{"stdout":"","stderr":"","error_code":0}}
+
+const { get_latest_runtime_matching_language_version } = require('../runtime');
+const { Job } = require('./job');
+const { body } = require('express-validator');
+
+module.exports = {
+
+    run_job_validators: [
+        body('language')
+            .isString(),
+        body('version')
+            .isString(),
+        // isSemVer requires it to be a version, not a selector
+        body('files')
+            .isArray(),
+        body('files.*.content')
+            .isString(),
+    ],
+
+    // POST /execute
+    async run_job(req, res) {
+        const runtime = get_latest_runtime_matching_language_version(req.body.language, req.body.version);
+
+        if (runtime === undefined) {
+            return res
+                .status(400)
+                .send({
+                    message: `${req.body.language}-${req.body.version} runtime is unknown`
+                });
+        }
+
+        const job = new Job({
+            runtime,
+            alias: req.body.language,
+            files: req.body.files,
+            args: req.body.args || [],
+            stdin: req.body.stdin || "",
+            timeouts: {
+                run: req.body.run_timeout || 3000,
+                compile: req.body.compile_timeout || 10000
+            }
+        });
+
+        await job.prime();
+
+        const result = await job.execute();
+
+        await job.cleanup();
+
+        return res
+            .status(200)
+            .send(result);
+    }
+
+};
diff --git a/api/src/index.js b/api/src/index.js
index 8a3fbb4..fcf82d6 100644
--- a/api/src/index.js
+++ b/api/src/index.js
@@ -9,6 +9,7 @@ const fs = require('fs/promises');
 const fss = require('fs');
 const body_parser = require('body-parser');
 const runtime = require('./runtime');
+const { validationResult } = require('express-validator');
 
 const logger = Logger.create('index');
 const app = express();
@@ -66,10 +67,48 @@ const app = express();
             })
       })
 
+    const validate = (req, res, next) => {
+        const errors = validationResult(req);
+
+        if (!errors.isEmpty()) {
+            return res
+                .status(400)
+                .send({
+                    message: errors.array()
+                });
+        }
+
+        next();
+    };
+
     logger.debug('Registering Routes');
 
-    const api_v1 = require('./api/v1')
-    app.use('/api/v1', api_v1); 
+    const ppman_routes = require('./ppman/routes');
+    const executor_routes = require('./executor/routes');
+
+    app.get('/api/v1/packages', ppman_routes.package_list);
+    app.post('/api/v1/packages/:language/:version', ppman_routes.package_install);
+    app.delete('/api/v1/packages/:language/:version', ppman_routes.package_uninstall);
+    app.post('/api/v1/execute',
+        executor_routes.run_job_validators,
+        validate,
+        executor_routes.run_job
+    );
+    app.get('/api/v1/runtimes', (req, res) => {
+        const runtimes = runtime
+            .map(rt => {
+                return {
+                    language: rt.language,
+                    version: rt.version.raw,
+                    aliases: rt.aliases,
+                    runtime: rt.runtime
+                };
+            });
+
+        return res
+            .status(200)
+            .send(runtimes);
+    });
 
     app.use(function (req,res,next){
         return res.status(404).send({message: 'Not Found'});
diff --git a/api/src/package.js b/api/src/ppman/package.js
similarity index 81%
rename from api/src/package.js
rename to api/src/ppman/package.js
index ec68f46..0e3c994 100644
--- a/api/src/package.js
+++ b/api/src/ppman/package.js
@@ -1,14 +1,14 @@
-const logger = require('logplease').create('package');
+const logger = require('logplease').create('ppman/package');
 const semver = require('semver');
-const config = require('./config');
-const globals = require('./globals');
+const config = require('../config');
+const globals = require('../globals');
 const fetch = require('node-fetch');
 const path = require('path');
 const fs = require('fs/promises');
 const fss = require('fs');
 const cp = require('child_process');
 const crypto = require('crypto');
-const runtime = require('./runtime');
+const runtime = require('../runtime');
 
 class Package {
 
@@ -158,38 +158,8 @@ class Package {
 
     }
 
-    static async get_package_list() {
-        const repo_content = await fetch(config.repo_url).then(x => x.text());
-    
-        const entries = repo_content
-            .split('\n')
-            .filter(x => x.length > 0);
-    
-        return entries.map(line => {
-            const [ language, version, checksum, download ] = line.split(',', 4);
-    
-            return new Package({
-                language,
-                version,
-                checksum,
-                download
-            });
-        });
-    }
-    
-    static async get_package (lang, version) {
-        const packages = await Package.get_package_list();
-    
-        const candidates = packages
-            .filter(pkg => {
-                return pkg.language == lang && semver.satisfies(pkg.version, version)
-            });
-    
-        candidates.sort((a, b) => semver.rcompare(a.version, b.version));
-    
-        return candidates[0] || null;
-    }
-
 }
 
-module.exports = Package;
+module.exports = {
+    Package
+};
diff --git a/api/src/ppman/routes.js b/api/src/ppman/routes.js
new file mode 100644
index 0000000..89f472c
--- /dev/null
+++ b/api/src/ppman/routes.js
@@ -0,0 +1,123 @@
+const logger = require('logplease').create('ppman/routes');
+const semver = require('semver');
+const fetch = require('node-fetch');
+const config = require('../config');
+const { Package } = require('./package');
+
+const get_package_list = async () => {
+    const repo_content = await fetch(config.repo_url).then(x => x.text());
+
+    const entries = repo_content
+        .split('\n')
+        .filter(x => x.length > 0);
+
+    return entries.map(line => {
+        const [ language, version, checksum, download ] = line.split(',', 4);
+
+        return new Package({
+            language,
+            version,
+            checksum,
+            download
+        });
+    });
+};
+
+const get_package = async (lang, version) => {
+    const packages = await get_package_list();
+
+    const candidates = packages
+        .filter(pkg => {
+            return pkg.language == lang && semver.satisfies(pkg.version, version)
+        });
+
+    candidates.sort((a, b) => semver.rcompare(a.version, b.version));
+
+    return candidates[0] || null;
+};
+
+module.exports = {
+
+    // GET /packages
+    async package_list(req, res) {
+        logger.debug('Request to list packages');
+
+        let packages = await get_package_list();
+
+        packages = packages
+            .map(pkg => {
+                    return {
+                        language: pkg.language,
+                        language_version: pkg.version.raw,
+                        installed: pkg.installed
+                    };
+                });
+
+        return res
+            .status(200)
+            .send(packages);
+    },
+
+    // POST /packages/:language/:version
+    async package_install(req, res) {
+        logger.debug('Request to install package');
+
+        const pkg = await get_package(req.params.language, req.params.version);
+
+        if (pkg == null) {
+            return res
+                .status(404)
+                .send({
+                    message: `Requested package ${req.params.language}-${req.params.version} does not exist`
+                });
+        }
+
+        try {
+            const response = await pkg.install();
+
+            return res
+                .status(200)
+                .send(response);
+        } catch(e) {
+            logger.error(`Error while installing package ${pkg.language}-${pkg.version}:`, e.message);
+
+            return res
+                .status(500)
+                .send({
+                    message: e.message
+                });
+        }
+    },
+
+    // DELETE /packages/:language/:version
+    async package_uninstall(req, res) {
+        logger.debug('Request to uninstall package');
+
+        const pkg = await get_package(req.params.language, req.params.version);
+
+        if (pkg == null) {
+            return res
+                .status(404)
+                .send({
+                    message: `Requested package ${req.params.language}-${req.params.version} does not exist`
+                });
+        }
+
+        try {
+            const response = await pkg.uninstall();
+
+            return res
+                .status(200)
+                .send(response);
+        } catch(e) {
+            logger.error(`Error while uninstalling package ${pkg.language}-${pkg.version}:`, e.message);
+
+            return res
+                .status(500)
+                .send({
+                    message: e.message
+                });
+        }
+    }
+
+};
diff --git a/packages/deno/1.7.5/metadata.json b/packages/deno/1.7.5/metadata.json
index d30608b..60b8a65 100644
--- a/packages/deno/1.7.5/metadata.json
+++ b/packages/deno/1.7.5/metadata.json
@@ -1,14 +1,5 @@
 {
     "language": "deno",
     "version": "1.7.5",
-    "provides": [
-        {
-            "language": "typescript",
-            "aliases": ["deno-ts","deno"]
-        },
-        {
-            "language": "javascript",
-            "aliases": ["deno-js"]
-        }
-    ]
+    "aliases": ["deno-ts", "deno-js"]
 }
diff --git a/packages/test.sh b/packages/test.sh
index 615e2db..9ba39ba 100755
--- a/packages/test.sh
+++ b/packages/test.sh
@@ -17,5 +17,5 @@ do
 
 	echo "==$test_file: $language-$lang_ver=="
 	#jq '.'  <<<"$result"
-	jq -r 'if (.run.stdout | contains("OK") ) then (.run.stdout) else (.compile.output + .run.output) end' <<<$result
+	jq -r '.compile.output + .run.output' <<<$result
 done
diff --git a/test.py b/test.py
new file mode 100644
index 0000000..b376c99
--- /dev/null
+++ b/test.py
@@ -0,0 +1 @@
+print('hello')