walnut ====== Small, light, and secure iot application framework. ```bash curl https://daplie.me/install-scripts | bash daplie-install-cloud ``` If the pretty url isn't working, for whatever reason, you also try the direct one ```bash # curl https://git.daplie.com/Daplie/daplie-snippets/raw/master/install.sh | bash # daplie-install-cloud ``` You could also, of course, try installing from the repository directly (especially if you have goldilocks or some similar already installed) ```bash mkdir -p /srv/walnut/ git clone git@git.daplie.com:Daplie/walnut.js.git /srv/walnut/core pushd /srv/walnut/core git checkout v1 popd bash /srv/walnut/core/install.sh ``` Features ------ * Works with Goldilocks for secure, Let's Encrypt maneged, https-only serving * IOT Application server written in [Node.js](https://nodejs.org) * Small memory footprint (for a node app) * Secure * Uses JWT, not Cookies\* * HTTPS-only (checks for X-Forwarded-For) * AES, RSA, and ECDSA encryption and signing * Safe against CSRF, XSS, and SQL injection * Safe against Compression attacks * Multi-Tentated Application Management * Built-in OAuth2 & OAuth3 support \*Cookies are used only for GETs and only where using a token would be less secure such as images which would otherwise require the token to be passed into the img src. They are also scoped such that CSRF attacks are not possible. In Progress ----------- * HTTPS Key Pinning * Heroku (pending completion of PostgreSQL support) * [GunDB](https://gundb.io) Support * OpenID support API --- API docs are here https://git.daplie.com/Daplie/com.example.hello Structure ===== Currently being tested with Ubuntu, Raspbian, and Debian on Digital Ocean, Raspberry Pi, and Heroku. ``` /srv/walnut/ ├── setup.sh (in-progress) ├── core │ ├── bin │ ├── boot │ ├── holepunch │ └── lib ├── etc │ └── client-api-grants ├── node_modules ├── packages │ ├── apis │ ├── pages │ └── services └── var └── sites ``` * `core` contains all walnut code * `node_modules` is a flat installation of all dependencies * `certs` is a directory for Let's Encrypt (or custom) certificates * `var` is a directory for database files and such * `packages` contains 3 types of packages Will install to --------------- ``` /srv/walnut/core/ /etc/walnut /opt/walnut /var/log/walnut /etc/systemd/system/walnut.service /etc/tmpfiles.d/walnut.conf ``` Implementation details ---------------- Initialization -------------- needs to know its primary domain ``` POST https://api./api/com.daplie.walnut.init { "domain": "" } ``` The following domains are required to point to WALNUT server ``` www. api. assets. cloud. api.cloud. ``` Example `/etc/goldilocks/goldilocks.yml`: ```yml tls: email: domains@example.com servernames: - example.com - www.example.com - api.example.com - assets.example.com - cloud.example.com - api.cloud.example.com http: trust_proxy: true modules: - name: proxy domains: - '*' address: '127.0.0.1:3000' ``` Resetting the Initialization ---------------------------- Once you run the app the initialization files will appear in these locations ``` /srv/walnut/var/com.daplie.walnut.config.sqlite3 /srv/walnut/config//config.json ``` Deleting those files will rese Accessing static apps --------------------- Static apps are stored in `packages/pages` ``` # App ID as files with a list of packages they should load # note that '#' is used in place of '/' because files and folders may not contain '/' in their names /srv/walnut/packages/sites/ # https://domain.tld/path /srv/walnut/packages/sites/ # https://domain.tld and https://domain.tld/foo match # packages are directories with reverse dns name # For the sake of debugging these packages can be accessed directly, without a site by /srv/walnut/packages/pages/ # matches apps./ and /apps/ ``` Accessing REST APIs ------------------- ``` # Apps are granted access to use a package by listing it in the grants file by the name of the app url (domain.tld) /srv/walnut/packages/client-api-grants/ # matches api./api/ and contains a list of allowed REST APIs # the REST apis themselves are submatched as api./api/ # packages are directories with reverse dns name, a package.json, and an index.js /srv/walnut/packages/rest/ ``` Example tree with contents: Here `com.example.hello` is a package with a REST API and a static page and `foobar.me` is a WALNUT-configured domain (smithfam.net, etc). The packages: ``` /srv/walnut/packages/ ├── api ├── pages │ └── com.example.hello │ └── index.html │ ''' │ │ com.example.hello │ │

com.example.hello

│ │ │ ''' │ ├── rest │ └── com.example.hello │ ├── package.json │ └── index.js │ ''' │ 'use strict'; │ │ module.exports.create = function (conf, deps, app) { │ │ app.use('/', function (req, res) { │ console.log('[com.example.hello] req.url', req.url); │ res.send({ message: 'hello' }); │ }); │ │ return deps.Promise.resolve(); │ }; │ │ ''' │ └── services ``` The permissions: ``` /srv/walnut/packages/ ├── client-api-grants │ └── cloud.foobar.me │ ''' │ com.example.hello # refers to /srv/walnut/packages/rest/com.example.hello │ ''' │ └── sites └── daplie.me ''' com.example.hello # refers to /srv/walnut/packages/pages/com.example.hello ''' ``` API --- ``` req.apiUrlPrefix => https://api.example.com/api/tld.domain.pkg ```