---
# System prepended metadata

title: PWA on the LibreRouter

---

# PWA on the LibreRouter

[Progressive Web Apps](https://en.wikipedia.org/wiki/Progressive_web_applications) provide a big set of features to webapps.
Some of them are critical for the [LiMe-App](https://github.com/libremesh/lime-app/), in particular accessing the geolocation, and the caching of the mobile app in the phone for faster loading.

In order for this features to be accessed, the app needs to be offered over SSL.

There is a very specific complication in relation to the SSL certificate: each router has one HTTP server, so each of them need to have an SSL certificate for their node.

There are different approaches to this:
1. Provide an invalid SSL certificate with each device: it can be that the certificate validity date has expired, is self-signed or it is overall invalid. The upside is that you can autogenerate one certificate per router and so keeping the communication secure. The downside is that there is a usability issue in relation to the screen that appears in the browser when you get an invalid SSL certificate, that makes the experience quite bad.
2. Provide a valid SSL certificate shared with all the devices: this can be done provisioning the SSL private cert with each device, and keeping it updated with an update mechanism. SSL certs have an expiration date as long as in 3 years, and the free ones have an expiration date of 3 months. This process deals with the usability issue of the previous approach (while the cert is kept valid via updates), but removes the secure communications with the router because the private key of the cert is public.

For the LibreRouter case, we will be using the SSL cert exclusively to be able to access the mobile phones's hardware features like geolocation, so we will go for the second option.

## Strategy

### Web server

We will be using an [uhttpd](https://openwrt.org/docs/guide-user/services/webserver/http.uhttpd) instance with uhttpd-mod-tls as described in [this guide](https://stokito.wordpress.com/2017/10/16/how-to-letsencrypt-https-on-openwrt-with-uhttpd/).

### SSL Certificate

We will be using the app.thisnode.info domain, so we separate the traffic from the usual local domain with this specific one that will be used for https traffic.

We will point the app.thisnode.info domain to a public server so we can share this cert with the routers.

The certificate will be retrieved using letsencrypt with a bot that will refresh the certificate every week, so when the nodes try to update they receive a long enough certificate every time.

As the post previously mentioned states, the certs provided by letsencrypt need to be converted in order to be used, so the cron script needs also to run:
```bash
certbot certonly --standalone --preferred-challenges tls-sni --cert-name=app.thisnode.info
openssl rsa -in /etc/letsencrypt/live/app.thisnode.info/privkey.pem -outform DER -out /var/www/app.thisnode.info.key
openssl x509 -in /etc/letsencrypt/live/app.thisnode.info/fullchain.pem -outform DER -out /var/www/app.thisnode.info.crt
```

And then by running any HTTP server on /var/www will be enough to publicly share this cert with the world.

### Update certificate on the router

This can be done when the certificate is one week old or more, each time the node gets internet.

In order to do that, we could trigger it by monitoring the `ip monitor route` output and reacting to new routes to 0.0.0.0/0. 
For now, we will use a 30 min cron action that will do a wget of the certs if the cert is old enough:
```
let AGE=$(($(date +%s)-$(date -r /etc/uhttpd.crt +%s)))
if [ $AGE -gt 604800 ]; do # 60seconds/m * 60minutes/h * 24hours/d * 7days
  wget http://app.thisnode.info/app.thisnode.info.key -O /etc/uhttpd.key
  wget http://app.thisnode.info/app.thisnode.info.crt -O /etc/uhttpd.crt
done
```

### Web service provided by the SSL app

The idea is to have a very narrow approach with this app, for the app to only be used when it is strictly necessary.
This decision is based on putting usability first: if the certificate goes invalid (because it is an offline network for example), only the functionality here will be affected.

Based on this decision, for now we will be using it for the geolocation.
An example can be found here: https://codepen.io/anon/pen/MMpXrm

```html
<html><body>
<h1>Set router location</h1>
<i class="fa fa-map-marker"></i>
<button id="locationbutton">Use my phone's location</button>
<div id="result"></div>
</body>
<script>
function getLocation(){
  var msg; 

  if('geolocation' in navigator){
    requestLocation();
  } else {
    msg = "Sorry, looks like your browser doesn't support geolocation";
    outputResult(msg);
  }

  function requestLocation(){
    var options = {
      enableHighAccuracy: false,
      timeout: 5000,
      maximumAge: 0
    };
  
    navigator.geolocation.getCurrentPosition(success, error, options); 
  
    function success(pos){
      var lng = pos.coords.longitude;
      var lat = pos.coords.latitude;

      msg = 'You appear to be at longitude: ' + lng + ' and latitude: ' + lat;
      //TODO send info to node
      outputResult(msg);
    }

    function error(err){
      msg = 'Error: ' + err + ' :(';
      outputResult(msg);
    }  
  }
  
  function outputResult(msg){
    document.getElementById("result").innerHTML=msg;
  }
}
document.getElementById("locationbutton").addEventListener('click', function(){
  getLocation();
});
</script>
</html>
```

## Server Sent Event receiver app for LimeApp

This app should work as an Android service, receive messages from Operating System, and popup the UI if needed.
As a base this can be used: https://medium.com/reactbrasil/how-to-create-an-unstoppable-service-in-react-native-using-headless-js-93656b6fd5d1