Compare commits
32 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e5d46b3dfb | |||
| 793d2a16d4 | |||
| 21aeb8e012 | |||
| d65415e13d | |||
| af9b386891 | |||
| cd7d3db11a | |||
| 1b45b24c3a | |||
| 7e9f89a9a1 | |||
| 6311878da0 | |||
| 856ed374be | |||
| 17b65d552f | |||
| f66adc0b65 | |||
| 1ad26e6266 | |||
| 2de7b7a826 | |||
| e0a8d75d69 | |||
| 235baf12e7 | |||
| 5506ee64e3 | |||
| 2bf8bb227d | |||
| 989a062e6e | |||
| f97fef3296 | |||
| c542f96670 | |||
| ee0e86124d | |||
| 289a4fc637 | |||
| 4c24e7a4e0 | |||
| 95e1bedff8 | |||
| f6b4bdd734 | |||
| a55c96e249 | |||
| 30bf637d5f | |||
| 9707e9d29e | |||
|
|
d09c5ac4d7 | ||
| 83a214881c | |||
| c29b083c01 |
|
|
@ -15,6 +15,8 @@ jobs:
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
- run: npm install -g bnycdn
|
- run: npm install -g bnycdn
|
||||||
- name: Setup Hugo
|
- name: Setup Hugo
|
||||||
uses: https://guardianproject.dev/actions/actions-hugo@v3
|
uses: https://guardianproject.dev/actions/actions-hugo@v3
|
||||||
|
|
|
||||||
BIN
assets/images/footer/essentials.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
assets/images/footer/gdpr.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
assets/images/logos/fdroid.webp
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
assets/team/images/alicja.webp
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
assets/team/images/question.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
|
|
@ -1,7 +1,3 @@
|
||||||
+++
|
+++
|
||||||
draft = false
|
draft = false
|
||||||
+++
|
+++
|
||||||
|
|
||||||
<div class="alert">
|
|
||||||
We are hiring! Check out our opening for a <a href="/posts/2026-hiring-python-developer/">backend Python developer</a>.
|
|
||||||
</div>
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ title = "Communication Strategy"
|
||||||
[params]
|
[params]
|
||||||
background = "images/backgrounds/new_tower.jpg"
|
background = "images/backgrounds/new_tower.jpg"
|
||||||
+++
|
+++
|
||||||
# Communication technology for challenging environments.
|
# Secure, robust and resilient technology for civil society.
|
||||||
### Software and systems to ensure your messages get through when they are the most critical.
|
### We build open source systems and processes to support human rights defenders, journalists, community groups and activists.
|
||||||
|
|
||||||
{{< primary-button name="Get in touch" url="/contact" icon="arrow-right" >}}
|
{{< primary-button name="Get in touch" url="/contact" icon="arrow-right" >}}
|
||||||
8
content/policies.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
title: Company Policies
|
||||||
|
date: 2026-04-22T11:00:00+00:00
|
||||||
|
type: page
|
||||||
|
---
|
||||||
|
|
||||||
|
* [Password and Authentication Policy](/policies/password_auth/)
|
||||||
|
* [Public WiFi Policy](/policies/public_wifi/)
|
||||||
181
content/posts/2025-using-tls-ech-from-python.md
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
---
|
||||||
|
title: "Using TLS ECH from Python"
|
||||||
|
date: 2025-01-10T13:00:00-00:00
|
||||||
|
tags:
|
||||||
|
- DEfO
|
||||||
|
- ECH
|
||||||
|
- OpenSSL
|
||||||
|
- Python
|
||||||
|
- TLS
|
||||||
|
params:
|
||||||
|
author: 'Iain Learmonth'
|
||||||
|
---
|
||||||
|
|
||||||
|
At first, the idea of encrypting more of the metadata found inside the initial packet (the "ClientHello") of a TLS
|
||||||
|
connection may seem simple and obvious, but there are of course reasons that this wasn't done right from the start.
|
||||||
|
In this post I will describe the flow of a connection using Encrypted Client Hello (ECH) to protect the metadata fields,
|
||||||
|
and present a working code example using a fork of CPython built with DEfO project's OpenSSL fork to connect to
|
||||||
|
ECH-enabled HTTPS servers.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
To understand why this is an issue, let's take a step back and look at how websites are hosted.
|
||||||
|
Many websites are hosted on shared servers, which means that a single server machine is responsible for serving
|
||||||
|
multiple, possibly hundreds or thousands, of websites.
|
||||||
|
This is known as the shared hosting model.
|
||||||
|
In this setup, when a user types in a URL or clicks on a link to visit a website and the browser connects to the server,
|
||||||
|
the server needs to know which website the users is requesting.
|
||||||
|
This is where the Server Name Indication (SNI) comes in - it's a field in the initial packet of a TLS connection that
|
||||||
|
tells the server which website the user is trying to access.
|
||||||
|
The server can then send the correct certificate so that the browser can authenticate the connection, and then send the
|
||||||
|
requested website content.
|
||||||
|
|
||||||
|
Because this field was sent unencrypted, this means that anyone who can see the traffic between the user's browser and
|
||||||
|
the server can intercept the SNI and know which website the user is trying to visit.
|
||||||
|
This can be a privacy concern, as it allows ISPs, network administrators, or other unwanted observers to build a profile
|
||||||
|
of the user's browsing history.
|
||||||
|
It's not just about the websites they visit, but also about the potential for censorship or targeted attacks.
|
||||||
|
With the SNI being unencrypted, it's like sending a postcard with the address visible to anyone who handles it - it may
|
||||||
|
not be the end of the world for most browsing activity, but it's certainly not private.
|
||||||
|
Encrypted Client Hello aims to change this by encrypting the SNI and other metadata, making it much harder for third
|
||||||
|
parties to intercept and exploit this information.
|
||||||
|
|
||||||
|
So, why wasn't it easy to protect the SNI and other metadata from the start?
|
||||||
|
The main challenge was that, in order to encrypt the SNI, the client (i.e., the user's browser) needs to know the
|
||||||
|
public key that the server wants the ClientHello to be encrypted with in advance.
|
||||||
|
However, the server's ECH public key is tied to the specific website being requested, and there wasn't a straightforward
|
||||||
|
way to discover a public key that could be used to talk to the server without revealing the SNI.
|
||||||
|
This created a chicken-and-egg problem, where the client couldn't encrypt the SNI without knowing the server's public
|
||||||
|
key, but it couldn't know the server's public key without sending the SNI in plaintext.
|
||||||
|
|
||||||
|
This problem is solved with ECH by introducing a new type of DNS record, called an
|
||||||
|
[HTTPS record](https://datatracker.ietf.org/doc/html/rfc9460).
|
||||||
|
An HTTPS record is a special type of DNS record that contains the ECH public key of the server, along with other metadata,
|
||||||
|
in a way that can be retrieved by the client without revealing the SNI (the website name is still leaked via the DNS
|
||||||
|
request, but it is possible to protect your requests using DNS-over-TLS or DNS-over-HTTPS).
|
||||||
|
The HTTPS record is typically retrieved by the client during the DNS lookup process, before the TLS connection is
|
||||||
|
established.
|
||||||
|
|
||||||
|
The HTTPS record contains an ECH configuration, which is used to encrypt the SNI and other metadata.
|
||||||
|
This is generated by the server and is tied to the specific configuration of the server, rather than to a specific
|
||||||
|
website.
|
||||||
|
By using HTTPS records to retrieve the server's ECH public key, we are able to break the chicken-and-egg problem and
|
||||||
|
provide a way to encrypt the SNI and other metadata.
|
||||||
|
|
||||||
|
Before we can lookup the HTTPS record, it's first necessary to work out where that record would live.
|
||||||
|
These records have been designed to be quite flexible, so can accommodate services running on non-default port numbers.
|
||||||
|
If the default port number is in use then the HTTPS record will be on the same domain name as the website, but for
|
||||||
|
non-default port numbers, there will be a prefix to the domain name:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def svcbname(url: str) -> str:
|
||||||
|
"""Derive DNS name of SVCB/HTTPS record corresponding to target URL."""
|
||||||
|
parsed = urllib.parse.urlparse(url)
|
||||||
|
if parsed.scheme == "https":
|
||||||
|
if (parsed.port or 443) == 443:
|
||||||
|
return parsed.hostname
|
||||||
|
else:
|
||||||
|
return f"_{parsed.port}._https.{parsed.hostname}"
|
||||||
|
elif parsed.scheme == "http":
|
||||||
|
if (parsed.port or 80) in (443, 80):
|
||||||
|
return parsed.hostname
|
||||||
|
else:
|
||||||
|
return f"_{parsed.port}._https.{parsed.hostname}"
|
||||||
|
else:
|
||||||
|
# For now, no other scheme is supported
|
||||||
|
return None
|
||||||
|
```
|
||||||
|
|
||||||
|
To keep it simple, the examples in this post will use plain DNS but the technique is equally applicable to DNS-over-TLS
|
||||||
|
and DNS-over-HTTPS. Now that we have the domain name to query, we can fetch the ECH configuration from the DNS using
|
||||||
|
the [dnspython](https://www.dnspython.org/) library:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_ech_configs(domain) -> List[bytes]:
|
||||||
|
try:
|
||||||
|
answers = dns.resolver.resolve(domain, "HTTPS")
|
||||||
|
except dns.resolver.NoAnswer:
|
||||||
|
logging.warning(f"No HTTPS record found for {domain}")
|
||||||
|
return []
|
||||||
|
except Exception as e:
|
||||||
|
logging.critical(f"DNS query failed: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
configs: List[bytes] = []
|
||||||
|
for rdata in answers:
|
||||||
|
if hasattr(rdata, "params"):
|
||||||
|
params = rdata.params
|
||||||
|
echconfig = params.get(5)
|
||||||
|
if echconfig:
|
||||||
|
configs.append(echconfig.ech)
|
||||||
|
if len(configs) == 0:
|
||||||
|
logging.warning(f"No echconfig found in HTTPS record for {domain}")
|
||||||
|
return configs
|
||||||
|
```
|
||||||
|
|
||||||
|
Once the ECH configurations are known, these can be used to establish the connection and fetch the website:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_http(url, ech_configs) -> bytes:
|
||||||
|
parser = urllib.parse.urlparse(url)
|
||||||
|
hostname, port, path = url.hostname, url.port, url.path
|
||||||
|
logging.debug("Performing GET request for https://{hostname}:{port}/{path}")
|
||||||
|
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||||
|
context.load_verify_locations(certifi.where())
|
||||||
|
for config in ech_configs:
|
||||||
|
try:
|
||||||
|
context.set_ech_config(config)
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
logging.error(f"SSL error: {e}")
|
||||||
|
pass
|
||||||
|
with socket.create_connection((hostname, port)) as sock:
|
||||||
|
with context.wrap_socket(sock, server_hostname=hostname, do_handshake_on_connect=False) as ssock:
|
||||||
|
try:
|
||||||
|
ssock.do_handshake()
|
||||||
|
logging.debug("Handshake completed with ECH status: %s", ssock.get_ech_status().name)
|
||||||
|
logging.debug("Inner SNI: %s, Outer SNI: %s", ssock.server_hostname, ssock.outer_server_hostname)
|
||||||
|
request = f'GET {path} HTTP/1.1\r\nHost: {hostname}\r\nConnection: close\r\n\r\n'
|
||||||
|
ssock.sendall(request.encode('utf-8'))
|
||||||
|
response = b''
|
||||||
|
while True:
|
||||||
|
data = ssock.recv(4096)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
response += data
|
||||||
|
return response
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
logging.error(f"SSL error: {e}")
|
||||||
|
raise e
|
||||||
|
```
|
||||||
|
|
||||||
|
The important step here is the new
|
||||||
|
[`set_ech_config`](https://irl.github.io/cpython/library/ssl.html#ssl.SSLContext.set_ech_config) method on the
|
||||||
|
`SSLContext` that allows you to add the ECH configuration containing the public key.
|
||||||
|
If there are multiple records, the underlying OpenSSL will determine which of the keys to use.
|
||||||
|
There are also a few new methods that allow you to get the status information relating to ECH from the `SSLSocket`
|
||||||
|
after the completion of the handshake.
|
||||||
|
|
||||||
|
In the simple case, that's all there is to it.
|
||||||
|
If you were to watch the connection with Wireshark you would not be able to see the true SNI being sent to the server
|
||||||
|
and would only see the decoy SNI present in the unencrypted "ClientHelloOuter".
|
||||||
|
This decoy SNI is added to appease [middleboxes](https://en.wikipedia.org/wiki/Middlebox) that may block traffic,
|
||||||
|
accidentally or deliberately, if that field is missing entirely.
|
||||||
|
There are also further protections against such middleboxes from the application of GREASE:
|
||||||
|
|
||||||
|
> If the client attempts to connect to a server and does not have an ECHConfig structure available for the server, it
|
||||||
|
> SHOULD send a GREASE "encrypted_client_hello" extension in the first ClientHello [...]
|
||||||
|
|
||||||
|
This means that if your client supports ECH but does not have the configuration available to use it, the client should
|
||||||
|
still send an ECH extension filled with nonsense anyway.
|
||||||
|
This will help to detect deployment issues early as errors will be immediately obvious to users and won't rely on
|
||||||
|
servers having deployed ECH before the errors are triggered.
|
||||||
|
|
||||||
|
Finally, if the server sees this GREASE ECH extension then it can use this to know that you support ECH but didn't
|
||||||
|
have a configuration available.
|
||||||
|
In its reply, it can send a "retry config" and then terminate the connection.
|
||||||
|
You then have the configuration available to start the connection again with a real ECH extension this time, and can
|
||||||
|
cache that for future requests too.
|
||||||
|
|
||||||
|
For a full client example including the use of retry configs, you can see our
|
||||||
|
[example Python client](https://github.com/defo-project/docker-defo-client/blob/main/pyclient.py) at GitHub.
|
||||||
|
You'll need to use this with our [CPython fork](https://github.com/irl/cpython) and
|
||||||
|
[OpenSSL fork](https://github.com/defo-project/openssl).
|
||||||
195
content/posts/2026-butter-box-connectivity/index.md
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
+++
|
||||||
|
title = 'Butter Box Connectivity'
|
||||||
|
date = 2026-04-13T20:00:00-00:00
|
||||||
|
lastmod = 2026-04-13T20:00:00-00:00
|
||||||
|
draft = false
|
||||||
|
tags = ['local','offline','wifi-halow','lora']
|
||||||
|
[params]
|
||||||
|
author = 'Iain Learmonth'
|
||||||
|
+++
|
||||||
|
|
||||||
|
We have just wrapped up a project with the [Guardian Project team](https://guardianproject.info/) exploring options for
|
||||||
|
connectivity to allow for updates to software and content on the
|
||||||
|
[Butter Box](https://likebutter.app/) and for communications between users of multiple Butter Boxes.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
We have explored two technologies:
|
||||||
|
|
||||||
|
* [LoRA](#lora)
|
||||||
|
* [WiFi HaLow](#wifi-halow)
|
||||||
|
|
||||||
|
Each of these has benefits and drawbacks and the choice of technology would be use case dependent.
|
||||||
|
|
||||||
|
We have not explored network or application layer protocols that would be run over these links, however the ecosystem
|
||||||
|
has many options to choose from for this whether it be IP-native protocols,
|
||||||
|
[Iroh](https://www.iroh.computer/)'s networking layer using [custom transports](https://docs.iroh.computer/transports/bluetooth#custom-transport-api),
|
||||||
|
[Willow](https://willowprotocol.org/)'s distributed synchronisation protocol,
|
||||||
|
or [Toosheh](https://www.netfreedompioneers.org/toosheh-datacasting-technology/)'s
|
||||||
|
content distribution and synchronisation protocol.
|
||||||
|
|
||||||
|
Until now, it has only been possible to update a Butter Box by physically replacing the USB drive with one with updated
|
||||||
|
content. We are hopeful that the learnings from this project will inform integration of an interconnection technology
|
||||||
|
into a future release.
|
||||||
|
|
||||||
|
## WiFi HaLow
|
||||||
|
|
||||||
|
Traditional WiFi operates at 2.4GHz and 5GHz, and aims to provide high speed connections between local devices.
|
||||||
|
The higher the speed, the higher the frequency of the carrier required, and pushes in this direction have led to the
|
||||||
|
development of the WiGig standard offering multi-gigabit speeds operating at 60GHz.
|
||||||
|
There is a tradeoff however: as the frequency increases the distance that the signal propagates and its ability to
|
||||||
|
propagate through materials both decrease.
|
||||||
|
|
||||||
|
WiFi HaLow is IEEE 802.11ah, a wireless protocol that takes WiFi and moves it down below 1 GHz. This is 863-870MHz in
|
||||||
|
ITU region 1 and some countries in region 3, and 902-928MHz in region 2.
|
||||||
|
Being in this lower frequency range means that signal can propagate further and will penetrate thicker and denser
|
||||||
|
materials like those found in urban environments.
|
||||||
|
|
||||||
|
To be compliant with the local rules governing radio spectrum use, the transmissions must be entirely contained within
|
||||||
|
the available frequency range, as for data transmissions there can be some "spillover" affecting nearby frequencies from
|
||||||
|
the signal.
|
||||||
|
In region 2, including the United States, there is a full 26MHz available allowing for 8MHz wide channels and a
|
||||||
|
theoretical maximum throughput of 43Mbps.
|
||||||
|
In regions 1 and 3 however there is only 7MHz available allowing for a 2MHz wide channel with maximum theoretical
|
||||||
|
throughput of 8.9Mbps.
|
||||||
|
|
||||||
|
The trade-off is straightforward: you sacrifice throughput for range.
|
||||||
|
HaLow promises roughly 10× the range and 100× the coverage area of conventional WiFi, which matters when you're rapidly
|
||||||
|
deploying an ad-hoc network responding to an evolving situation.
|
||||||
|
It is not a replacement for the WiFi that carries video calls, but rather a means of tactical communication and
|
||||||
|
dissemination of updates received from outside the network, allowing for the exchange of critical messaging and
|
||||||
|
maintaining situational awareness for responders during communications outages.
|
||||||
|
|
||||||
|
In our evaluation of WiFi HaLow, we used the Morse Micro
|
||||||
|
[MM8108-EKH19 Evaluation Kit](https://www.morsemicro.com/resources/product_brief/MM8108-EKH19-Product-Brief.pdf) which
|
||||||
|
is currently available for purchase
|
||||||
|
[from Mouser Electronics](https://www.mouser.co.uk/ProductDetail/Morse-Micro/MM8108-EKH19-01?qs=HMhDvBYWqvOSMbg%2FkiOXMw%3D%3D)
|
||||||
|
for £155.18 ex. VAT (at time of writing).
|
||||||
|
|
||||||
|
{{< figure
|
||||||
|
src="/images/2026/halow-powerbank.png"
|
||||||
|
alt="A WiFi router with an additional USB dongle with an antenna attached to it velcroed to a USB powerbank."
|
||||||
|
caption="Our portable WiFi HaLow bridge"
|
||||||
|
>}}
|
||||||
|
|
||||||
|
We constructed a network using two evaluation kits, one placed in our second floor office next to a window that faces
|
||||||
|
down a street, and the other velcroed to a powerbank allowing it to be easier moved down the street for measurements
|
||||||
|
to be taken of signal strength and negotiated link speed between the two stations.
|
||||||
|
|
||||||
|
Test traffic would be generated over the link with an mobile device connecting to the mobile station, via conventional
|
||||||
|
WiFi, which would use the WiFi HaLow bridge to communicate with a Butter Box back in the office.
|
||||||
|
Another mobile device was connected to the office station via conventional WiFi so that the two devices could exchange
|
||||||
|
end-to-end encrypted messages using the [Delta Chat](https://delta.chat/) relay hosted on the Butter Box.
|
||||||
|
|
||||||
|
In order to determine the theoretical performance before conducting practical experiments, we did model the expected
|
||||||
|
received signal strength around the office station:
|
||||||
|
|
||||||
|
{{< figure
|
||||||
|
src="/images/2026/halow-coverage.png"
|
||||||
|
alt="A coverage map showing that WiFi HaLow is not as affected by buildings as traditional 2.4 and 5GHz WiFi, with good coverage stretching approximately 400 meters in all directions from the access point site."
|
||||||
|
caption="Coverage map for the test WiFi HaLow access point at the SR2 office"
|
||||||
|
>}}
|
||||||
|
|
||||||
|
The colours in this diagram change as signal strength decreases moving away from the office, from red through yellow,
|
||||||
|
green, and blue. From the data sheet, we know the expected link speed achievable for each level of received signal
|
||||||
|
strength:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<caption>PHY data rate (Mbps) / Minimum receive sensitivity (dBm)</caption>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">1 MHz Channel</th>
|
||||||
|
<th colspan="2">2 MHz Channel</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>0.1 Mbps</td>
|
||||||
|
<td>-107 dBm</td>
|
||||||
|
<td colspan="2">—</td>
|
||||||
|
</tr>
|
||||||
|
<tr><td>0.3 Mbps</td><td>-106 dBm</td><td>0.7 Mbps</td><td>-103 dBm</td></tr>
|
||||||
|
<tr><td>0.7 Mbps</td><td>-104 dBm</td><td>1.4 Mbps</td><td>-101 dBm</td></tr>
|
||||||
|
<tr><td>1.0 Mbps</td><td>-102 dBm</td><td>2.2 Mbps</td><td>-99 dBm</td></tr>
|
||||||
|
<tr><td>1.3 Mbps</td><td>-99 dBm</td><td>2.9 Mbps</td><td>-96 dBm</td></tr>
|
||||||
|
<tr><td>2.0 Mbps</td><td>-96 dBm</td><td>4.3 Mbps</td><td>-93 dBm</td></tr>
|
||||||
|
<tr><td>2.7 Mbps</td><td>-92 dBm</td><td>5.8 Mbps</td><td>-89 dBm</td></tr>
|
||||||
|
<tr><td>3.0 Mbps</td><td>-90 dBm</td><td>6.5 Mbps</td><td>-87 dBm</td></tr>
|
||||||
|
<tr><td>3.3 Mbps</td><td>-89 dBm</td><td>7.2 Mbps</td><td>-86 dBm</td></tr>
|
||||||
|
<tr><td>4.0 Mbps</td><td>-85 dBm</td><td>8.9 Mbps</td><td>-82 dBm</td></tr>
|
||||||
|
<tr><td>4.4 Mbps</td><td>-83 dBm</td><td colspan="2">—</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Our test path was to the left of the office on the map above, towards "West End".
|
||||||
|
We expected reliable connectivity at a speed >2 Mbps up until we were approaching the threshold where we expected to
|
||||||
|
drop below 96 dBm, where the greens start to turn into blues approximately 500 meters from the office.
|
||||||
|
What we found, however, was that the negotiated link speed of the bridge was volatile even just 50 meters down the road.
|
||||||
|
We would frequently see the link speeds drop to 0.3 Mbps before recovering tens of seconds later.
|
||||||
|
This is obviously not the ideal environment being an urban environment with vehicular traffic and plenty of other
|
||||||
|
devices competing for spectrum. (Check out the
|
||||||
|
[Morse Micro YouTube channel](https://www.youtube.com/@morsemicro) if you want to see some ideal environment tests, like
|
||||||
|
this one [achieving 3km range along Ocean Beach, California](https://www.youtube.com/watch?v=2xlUijXucoM)).
|
||||||
|
|
||||||
|
At 500 meters it was still possible, albeit frustratingly slow at times, to access the Butter Box interface over the
|
||||||
|
bridge. At that distance, it would be practical to have a second Butter Box to provide an interface and Delta Chat
|
||||||
|
relay, and then allow the relays to communicate over the bridge but keep other interactions local. Delta Chat is built
|
||||||
|
on email which was designed to be delay tolerant from the start and so is well suited for this use case.
|
||||||
|
|
||||||
|
WiFi HaLow operates in an ISM (industrial, scientific, and medical) band. This is a special allocation where the device
|
||||||
|
is certified but the individual operator does not need a licence themselves, as they would with marine, air, or land
|
||||||
|
mobile radio equipment. Part of this certification is that the equipment must cooperate with other spectrum users and
|
||||||
|
this is implemented in WiFi HaLow, as with conventional WiFi, with a "listen before send" test. If another device is
|
||||||
|
sending data then the device must wait for the channel to be clear until it is able to transmit.
|
||||||
|
|
||||||
|
Unfortunately due to weather we did not have many opportunities to field test the equipment, so do not have comparable
|
||||||
|
data for less dense environments or environments with lower vehicular traffic. One of the things we note is very common
|
||||||
|
on the 868MHz band, at least in the UK, is vehicle tyre pressure sensors that are communicating from the spinning tyre
|
||||||
|
back to the car to alert drivers to low pressure.
|
||||||
|
|
||||||
|
## LoRa
|
||||||
|
|
||||||
|
LoRa (from "long range") is a physical layer radio modulation technique based on chirp spread spectrum.
|
||||||
|
It encodes data by sweeping radio signals across frequencies making the signal resistant to interference and capable of
|
||||||
|
travelling greater distances at lower power.
|
||||||
|
|
||||||
|
LoRa itself is not a complete network stack and only provides the physical radio characteristics.
|
||||||
|
It is not possible to build a fully open source LoRa system as its physical layer is a proprietary standard owned by
|
||||||
|
Semtech.
|
||||||
|
The techniques that make LoRa function are locked behind semiconductor licensing.
|
||||||
|
|
||||||
|
Like WiFi HaLow, it uses the 868MHz/915MHz bands, but also has the option of a second band at 433MHz.
|
||||||
|
While you're more likely to get a range gain from using a better antenna or feeder than from shifting from the 868MHz to
|
||||||
|
433MHz band, the ISM band at 433MHz overlaps with the 70cm amateur radio band in regions 1 and 2, and some region 3
|
||||||
|
countries.
|
||||||
|
This overlap has led to the production of hardware with transmit power capability than would be allowed when operating
|
||||||
|
as an ISM device, and this higher power often produces very impressive range results.
|
||||||
|
|
||||||
|
The tradeoff here is more extreme, but it is the same as for WiFi HaLow, you sacrifice throughput for range.
|
||||||
|
For our testing we used a pair of RS232 to LoRa bridges purchased from AliExpress.
|
||||||
|
These are available for just £25.91 ex. VAT each at time of writing, considerably cheaper than the WiFi HaLow kit.
|
||||||
|
They were labelled as "USR-LG206-P" and while we were unable to obtain a datasheet, the listing stated that we would get
|
||||||
|
2680 bits per second. Not megabits, not kilobits, just bits.
|
||||||
|
|
||||||
|
We confirmed that data could indeed be sent over greater distances than WiFi HaLow in our initial testing however this
|
||||||
|
throughput was not suitable to attempt to load the Butter Box portal.
|
||||||
|
One possibility for future exploration would be to run UUCP over the emulated serial link.
|
||||||
|
The necessary software for this [is maintained in Debian to this day](https://packages.debian.org/sid/uucp) and would
|
||||||
|
be ready for configuration in a custom Delta Chat relay configuration.
|
||||||
|
|
||||||
|
Projects like [tinySSB](https://github.com/ssbc/tinySSB) recognise the severe limitations of LoRa and have adapted their
|
||||||
|
protocols to the lower throughput rate, including enhanced compression, smaller packet sizes, and even ignoring some of
|
||||||
|
the restrictions that are placed on ISM band users like [duty cycle](https://en.wikipedia.org/wiki/Duty_cycle)
|
||||||
|
restrictions.
|
||||||
|
Butter Box could act as a node in these kinds of mesh networks, but ultimately these systems serve different purposes.
|
||||||
|
|
||||||
|
## The Verdict
|
||||||
|
|
||||||
|
LoRa has superior range and the equipment may be more widely available at a cheaper price, but the throughput is low
|
||||||
|
enough that it requires protocols that have been specifically designed with these slow speeds and high latency in mind.
|
||||||
|
Even transmitting low resolution images over these kinds of links would tie them up for minutes at a time.
|
||||||
|
If you need to get smaller amounts of data over greater distances then it's definitely a great technology but for the
|
||||||
|
Butter Box it does not compliment any existing functionality.
|
||||||
|
|
||||||
|
WiFi HaLow may allow a Butter Box deployment to spread across a school campus, refugee camp, or evacuation centre with
|
||||||
|
less equipment required for a rapid deployment. Updates provided to one Butter Box could make their way through a
|
||||||
|
network of Butter Boxes at reasonable speeds. We believe that in a less dense environment, with fewer devices competing
|
||||||
|
for that radio spectrum, the performance would be more reliable however we were not able to fully explore this.
|
||||||
|
For many deployments though, the current cost of the hardware may be prohibitive. We are hopeful that the price of this
|
||||||
|
will come down as Morse Micro moves from the evaluation kits towards production quality implementations.
|
||||||
85
content/posts/2026-butter-box-portal/index.md
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
+++
|
||||||
|
title = 'Butter Box Portal Improvements'
|
||||||
|
date = 2026-04-15T16:00:00-00:00
|
||||||
|
lastmod = 2026-04-15T16:00:00-00:00
|
||||||
|
draft = false
|
||||||
|
tags = ['local','offline','butterbox', 'deltachat']
|
||||||
|
[params]
|
||||||
|
author = 'Ana Custura'
|
||||||
|
+++
|
||||||
|
|
||||||
|
As part of our latest development project with the [Guardian Project team](https://guardianproject.info/), we have
|
||||||
|
re-engineered the [Butter Box](https://likebutter.app/) portal interface. This post describes the design choices and improvements within the new
|
||||||
|
portal.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
## Portal tech stack
|
||||||
|
|
||||||
|
Previously, the interface was a static site built with [Jekyll](https://jekyllrb.com/), which offered no customisation
|
||||||
|
options and was ill-suited for the portal's dynamic requirements. It has now been replaced with a [Python Flask](https://flask.palletsprojects.com/en/stable/)
|
||||||
|
application, a lightweight framework that allows developers to include only the necessary libraries, [such as for
|
||||||
|
localisation](https://python-babel.github.io/flask-babel/), minimising the application's footprint.
|
||||||
|
|
||||||
|
We are now also using [Bulma CSS](https://bulma.io/) to style it, a free and open source framework that is lightweight
|
||||||
|
and JS free, designed for mobile applications, which was also chosen with reducing the size of the portal app in mind.
|
||||||
|
|
||||||
|
## Portal customisation
|
||||||
|
|
||||||
|
In contrast, the new portal features the ability to change the logo and modify the display name of a Butter Box, making
|
||||||
|
it more customisable. This allows different communities to deploy the box in a way that better aligns with their
|
||||||
|
identity and fosters greater trust with their users.
|
||||||
|
|
||||||
|
{{< figure
|
||||||
|
src="/images/2026/portal-branding.png"
|
||||||
|
alt="A screenshot of the Butter Box portal including fields to change the name and logo."
|
||||||
|
caption="The Butter Box portal boasts new customisation capabilities."
|
||||||
|
>}}
|
||||||
|
|
||||||
|
We've also made improvements to allow users to configure Butter Box security with minimal effort. This includes changing the
|
||||||
|
admin password for the interface, modifying the Wi-Fi name, setting a Wi-Fi password, changing the root password,
|
||||||
|
and controlling SSH behavior through the portal.
|
||||||
|
|
||||||
|
{{< figure
|
||||||
|
src="/images/2026/portal-security.png"
|
||||||
|
alt="A screenshot of the Butter Box portal including fields to change the root and admin passwords and SSH behaviour."
|
||||||
|
caption="The Butter Box portal also features new security capabilities."
|
||||||
|
>}}
|
||||||
|
|
||||||
|
We now also allow setting a date and time for the box through the portal. This functionality is essential for supporting
|
||||||
|
more advanced applications in the future, particularly those involving cryptography, which require accurate time
|
||||||
|
synchronisation. Note that the Raspberry Pi does not have an internal real-time clock module, so manual time
|
||||||
|
configuration is necessary in the absence of the Internet.
|
||||||
|
|
||||||
|
Future plans include a customizable welcome message on the portal landing page and the ability for administrators to
|
||||||
|
upload a custom background image. These enhancements will expand branding options for organizations deploying the box.
|
||||||
|
|
||||||
|
|
||||||
|
## Integrating DeltaChat
|
||||||
|
|
||||||
|
The portal now includes a dedicated page that allows users to download the [DeltaChat](https://delta.chat/en/) APK for
|
||||||
|
Android devices and securely register an account on a locally running relay. During registration, a randomly generated
|
||||||
|
username and password are provided.
|
||||||
|
|
||||||
|
{{< figure
|
||||||
|
src="/images/2026/portal-deltachat.png"
|
||||||
|
alt="A screenshot of the Butter Box portal DeltaChat account registration page."
|
||||||
|
caption="DeltaChat messaging through a local relay is now supported by Butter Box."
|
||||||
|
>}}
|
||||||
|
|
||||||
|
This enhancement would in future enable the connection of multiple relays running on separate boxes, to ultimately allow
|
||||||
|
sending messages between communities.
|
||||||
|
|
||||||
|
## Portal updates and future
|
||||||
|
|
||||||
|
An advantage of using [Debian](https://en.wikipedia.org/wiki/Debian) as the OS for Butter Box is the ability to create
|
||||||
|
a content pack with a local [Debian mirror](https://www.debian.org/mirror/ftpmirror), enabling the box to be updated
|
||||||
|
without an Internet connection.
|
||||||
|
|
||||||
|
The future plan for the portal is therefore to package it as a Debian package, enabling updates through the Butter Box
|
||||||
|
OS’s native package management. Updates to any APKs distributed via the portal, such as the Delta Chat APK, would also
|
||||||
|
be delivered through upgrading the portal, and will not require re-flashing a new image onto the box.
|
||||||
|
|
||||||
|
Ultimately, as a result of portal improvements the Butter Box is now a more flexible, secure, and upgradable platform,
|
||||||
|
and the groundwork has been laid for enabling future capabilities like cross-box messaging and time-sensitive
|
||||||
|
cryptographic applications.
|
||||||
53
content/posts/2026-cyber-essentials/index.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
+++
|
||||||
|
title = 'SR2 Communications Achieves Cyber Essentials Certification'
|
||||||
|
date = 2026-05-03T09:20:00-00:00
|
||||||
|
lastmod = 2026-05-03T09:20:00-00:00
|
||||||
|
draft = false
|
||||||
|
tags = ['security', 'audit']
|
||||||
|
[params]
|
||||||
|
author = 'Iain Learmonth'
|
||||||
|
+++
|
||||||
|
|
||||||
|
We're pleased to announce that SR2 Communications has achieved
|
||||||
|
[Cyber Essentials](https://www.ncsc.gov.uk/cyberessentials/overview) certification, the UK government's baseline
|
||||||
|
standard for cyber security.
|
||||||
|
This milestone represents an important addition to our existing security practices and reinforces our dedication to
|
||||||
|
protecting the organisations we serve.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
<figure>
|
||||||
|
<img src="/images/2026/cyber-essentials.png" alt="Certificate of Assurance - SR2 Group Limited, incorporating SR2 Communications Limited and SR2 Professional Services Limited, complies with the requirements of the Cyber Essentials scheme">
|
||||||
|
<figcaption>Our Cyber Essentials Certificate</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
Cyber Essentials is a government-backed certification scheme developed by the National Cyber Security Centre (NCSC).
|
||||||
|
It establishes five core technical controls designed to prevent the most common cyber security threats.
|
||||||
|
According to NCSC, organisations with this certification are protected against approximately 80% of the most common
|
||||||
|
cyber attacks that they have observed.
|
||||||
|
|
||||||
|
We've always had a strong focus on security (it's the S in SR2!) and have always maintained rigorous security practices
|
||||||
|
in our software development and infastructure hosting including external audits of application code and periodic
|
||||||
|
penetration testing of our infrastructure. These practices remain in place and will continue to provide project-specific
|
||||||
|
assurance. However, Cyber Essentials addresses something equally critical: the foundational security of our
|
||||||
|
organisation.
|
||||||
|
|
||||||
|
While code audits and pentests examine specific systems and software, Cyber Essentials evaluates how we operate as an
|
||||||
|
organisation, covering five primary areas:
|
||||||
|
|
||||||
|
* Boundary firewalls and internet gateways
|
||||||
|
* Secure configuration
|
||||||
|
* User access control
|
||||||
|
* Malware protection
|
||||||
|
* Patch management
|
||||||
|
|
||||||
|
This certification ensures that the foundation upon which our technical work rests is equally secure. The organisations
|
||||||
|
we work with include free software projects, charities, non-profits, advocacy groups, and the media.
|
||||||
|
They often handle sensitive data related to vulnerable populations, campaign strategies, and confidential stakeholder
|
||||||
|
information. They need partners they can trust.
|
||||||
|
|
||||||
|
For those partners operating with limited resources, knowing that their technology partners meet recognised security
|
||||||
|
standards removes one more concern from their already demanding work.
|
||||||
|
|
||||||
|
If your organisation is looking for a technology partner that understands your mission and takes security seriously,
|
||||||
|
we'd welcome you to [get in touch](/contact).
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
+++
|
+++
|
||||||
title = 'Python Backend Developer Opening'
|
title = 'Python Backend Developer Opening'
|
||||||
date = 2026-02-08T13:00:00-00:00
|
date = 2026-02-08T13:00:00-00:00
|
||||||
lastmod = 2026-02-14T10:00:00-00:00
|
lastmod = 2026-03-05T10:00:00-00:00
|
||||||
draft = false
|
draft = false
|
||||||
tags = ['job','python']
|
tags = ['job','python']
|
||||||
[params]
|
[params]
|
||||||
|
|
@ -12,6 +12,8 @@ SR2 Communications develops technology to support individuals, journalism public
|
||||||
with their digital security needs. This ranges from secure hosting of an off-the-shelf application to bespoke
|
with their digital security needs. This ranges from secure hosting of an off-the-shelf application to bespoke
|
||||||
development of novel software to fill a niche requirement.
|
development of novel software to fill a niche requirement.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
We are searching for a Python developer to join our team to work on a backend application.
|
We are searching for a Python developer to join our team to work on a backend application.
|
||||||
The application will use the FastAPI framework and communicate with a PostgreSQL database and third-party APIs.
|
The application will use the FastAPI framework and communicate with a PostgreSQL database and third-party APIs.
|
||||||
The application uses OpenID Connect for authentication.
|
The application uses OpenID Connect for authentication.
|
||||||
|
|
@ -37,7 +39,9 @@ Initially the position is for 4 months, however we expect to be able to extend t
|
||||||
|
|
||||||
## How to apply
|
## How to apply
|
||||||
|
|
||||||
Send your CV with cover letter by email to contact@sr2.uk. Please do not use a LLM when constructing your CV or cover
|
~~Send your CV with cover letter by email to contact@sr2.uk. Please do not use a LLM when constructing your CV or cover
|
||||||
letter.
|
letter.~~
|
||||||
|
|
||||||
We will build a shortlist and invite selected candidates to interview.
|
~~We will build a shortlist and invite selected candidates to interview.~~
|
||||||
|
|
||||||
|
**This position has now closed.**
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = 'Support'
|
title = 'Support'
|
||||||
date = 2026-02-03T08:00:00-07:00
|
date = 2026-05-03T10:30:00-00:00
|
||||||
description = 'Customer support contact methods and service level objectives.'
|
description = 'Customer support contact methods and service level objectives.'
|
||||||
type = "page"
|
type = "page"
|
||||||
+++
|
+++
|
||||||
|
|
@ -9,6 +9,8 @@ You can get support directly from our staff.
|
||||||
For most customers the most efficient way to see your issue resolved will be to contact us via our support system or by
|
For most customers the most efficient way to see your issue resolved will be to contact us via our support system or by
|
||||||
email to contact@sr2.uk.
|
email to contact@sr2.uk.
|
||||||
|
|
||||||
|
OpenPGP fingerprint: [`1135 3E54 83C7 152B 165C 46A7 9CE7 365E C2E1 4728`](/helpdesk.asc)
|
||||||
|
|
||||||
Below you can also find additional options for support, and what you can expect when you contact us.
|
Below you can also find additional options for support, and what you can expect when you contact us.
|
||||||
|
|
||||||
### 1. Service Level Objective
|
### 1. Service Level Objective
|
||||||
|
|
|
||||||
10
content/team/alicja-mlek.md
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
+++
|
||||||
|
date = '2026-04-02T15:48:38+01:00'
|
||||||
|
draft = false
|
||||||
|
name = 'Alicja Mlek'
|
||||||
|
[params]
|
||||||
|
pronoun = '(she/her)'
|
||||||
|
education = 'AICB'
|
||||||
|
role = 'Office Manager'
|
||||||
|
photo = 'team/images/alicja.webp'
|
||||||
|
+++
|
||||||
10
content/team/chris-milne.md
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
+++
|
||||||
|
date = '2026-04-02T15:48:38+01:00'
|
||||||
|
draft = false
|
||||||
|
name = 'Chris Milne'
|
||||||
|
[params]
|
||||||
|
pronoun = '(he/him)'
|
||||||
|
education = ''
|
||||||
|
role = 'Backend Developer'
|
||||||
|
photo = 'team/images/question.png'
|
||||||
|
+++
|
||||||
|
|
@ -51,6 +51,7 @@ NCAGE: U2G06'''
|
||||||
[languages.en.params.footer.col2]
|
[languages.en.params.footer.col2]
|
||||||
items = [
|
items = [
|
||||||
{title = 'Information'},
|
{title = 'Information'},
|
||||||
|
{text = 'Service Status', href='https://status.sr2.uk/', status=true},
|
||||||
{text = 'Customer Support', href = '/support'},
|
{text = 'Customer Support', href = '/support'},
|
||||||
{text = 'Reporting Abuse', href = '/abuse'},
|
{text = 'Reporting Abuse', href = '/abuse'},
|
||||||
{text = 'Visiting Us', href = '/visiting'},
|
{text = 'Visiting Us', href = '/visiting'},
|
||||||
|
|
@ -59,13 +60,17 @@ NCAGE: U2G06'''
|
||||||
{text = 'Terms and Conditions', href = '/terms'},
|
{text = 'Terms and Conditions', href = '/terms'},
|
||||||
{text = 'Privacy Policy', href = '/privacy'},
|
{text = 'Privacy Policy', href = '/privacy'},
|
||||||
{text = 'Complaints Policy', href = '/complaints'},
|
{text = 'Complaints Policy', href = '/complaints'},
|
||||||
|
{text = 'Other Policies', href='/policies'},
|
||||||
]
|
]
|
||||||
|
|
||||||
[languages.en.params.footer.col3]
|
[languages.en.params.footer.col3]
|
||||||
items = [
|
items = [
|
||||||
{title = 'Social'},
|
{title = 'Social'},
|
||||||
|
{text = 'Open Collective', icon = 'circle', href='https://opencollective.com/sr2comm'},
|
||||||
{text = 'Git', icon = 'git-branch', href = 'https://guardianproject.dev/sr2'},
|
{text = 'Git', icon = 'git-branch', href = 'https://guardianproject.dev/sr2'},
|
||||||
|
{text = 'Bluesky', icon = 'at-sign', href = 'https://bsky.app/profile/sr2.uk'},
|
||||||
{text = 'LinkedIn', icon = 'linkedin', href = 'https://www.linkedin.com/company/sr2uk/'},
|
{text = 'LinkedIn', icon = 'linkedin', href = 'https://www.linkedin.com/company/sr2uk/'},
|
||||||
|
{logo = 'images/footer/essentials.png', href = "/cyber-essentials.pdf"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[params.styles]
|
[params.styles]
|
||||||
|
|
@ -85,3 +90,6 @@ NCAGE: U2G06'''
|
||||||
color_gradient_end = '#009A64'
|
color_gradient_end = '#009A64'
|
||||||
color_text = "#222"
|
color_text = "#222"
|
||||||
bp_mobile = '768px'
|
bp_mobile = '768px'
|
||||||
|
|
||||||
|
[markup.goldmark.renderer]
|
||||||
|
unsafe = true
|
||||||
78
layouts/_partials/head/scripts.html
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
{{ if eq .Name "Contact" }}
|
||||||
|
<script
|
||||||
|
id="zammad_form_script"
|
||||||
|
src="https://help.sr2.uk/assets/form/form.js"></script>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
$("#zammad-feedback-form").ZammadForm({
|
||||||
|
agreementMessage: {{ site.Params.feedback.agreementMessage }},
|
||||||
|
messageSubmit:{{ site.Params.feedback.messageSubmit }},
|
||||||
|
messageThankYou:{{ site.Params.feedback.messageThankYou }},
|
||||||
|
showTitle: false,
|
||||||
|
noCSS: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
var cStateAPIRoot = 'https://status.sr2.uk/index.json'
|
||||||
|
var cStateDotTargetElement = document.querySelector('.status-indicator');
|
||||||
|
|
||||||
|
// Only change this if you are hacking around! :)
|
||||||
|
var cStateEmbedPrefix = '[cState HTML Embed v2.0] ';
|
||||||
|
var cStateEmbedDebugging = false;
|
||||||
|
var cStateAPIStatus = 'tryingToGetStatus';
|
||||||
|
|
||||||
|
fetch(cStateAPIRoot)
|
||||||
|
.then(
|
||||||
|
function(response) {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log(cStateEmbedPrefix + 'API not OK, it sent HTTP status code ' +
|
||||||
|
response.status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Examine the text in the response
|
||||||
|
response.json().then(function(data) {
|
||||||
|
cStateAPIStatus = data.summaryStatus;
|
||||||
|
|
||||||
|
// When debugging, this code should be run to see API response
|
||||||
|
if (cStateEmbedDebugging) {
|
||||||
|
console.log(cStateEmbedPrefix + 'API response: ', data);
|
||||||
|
console.log(cStateEmbedPrefix + 'API says status page is: ' + cStateAPIStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI code (run even if not debugging)
|
||||||
|
// You can change how the dot appears here
|
||||||
|
cStateDotTargetElement.style.display = 'inline-block';
|
||||||
|
cStateDotTargetElement.style.height = '10px';
|
||||||
|
cStateDotTargetElement.style.width = '10px';
|
||||||
|
cStateDotTargetElement.style.borderRadius = '50%';
|
||||||
|
cStateDotTargetElement.setAttribute('aria-label', 'Status indicator');
|
||||||
|
cStateDotTargetElement.style.backgroundColor = '#333';
|
||||||
|
// Sets color and accessible label
|
||||||
|
if (cStateAPIStatus === 'ok') {
|
||||||
|
cStateDotTargetElement.style.backgroundColor = '#4caf50';
|
||||||
|
cStateDotTargetElement.setAttribute('aria-label', 'Green icon indicating no issues');
|
||||||
|
} else if (cStateAPIStatus === 'notice') {
|
||||||
|
cStateDotTargetElement.style.backgroundColor = '#607d8b';
|
||||||
|
cStateDotTargetElement.setAttribute('aria-label', 'Gray icon asking users to check status page');
|
||||||
|
} else if (cStateAPIStatus === 'disrupted') {
|
||||||
|
cStateDotTargetElement.style.backgroundColor = '#ff9800';
|
||||||
|
cStateDotTargetElement.setAttribute('aria-label', 'Orange icon indicating disruptions');
|
||||||
|
} else { // down
|
||||||
|
cStateDotTargetElement.style.backgroundColor = '#82071e';
|
||||||
|
cStateDotTargetElement.setAttribute('aria-label', 'Red icon indicating downtime');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch(function(err) {
|
||||||
|
console.log('Fetch error :-S', err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -6,13 +6,6 @@
|
||||||
|
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
{{ $ctx := dict
|
|
||||||
"page" .
|
|
||||||
"title" (T "Our Team")
|
|
||||||
"content" (partial "team.html" .)
|
|
||||||
}}
|
|
||||||
{{ partial "flex-section.html" $ctx }}
|
|
||||||
|
|
||||||
{{ $ctx := dict
|
{{ $ctx := dict
|
||||||
"page" .
|
"page" .
|
||||||
"title" (T "Our Partners")
|
"title" (T "Our Partners")
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
<img style="max-height: 100px; max-width: 100%;" src="{{ .RelPermalink }}" alt="Guardian Project">
|
<img style="max-height: 100px; max-width: 100%;" src="{{ .RelPermalink }}" alt="Guardian Project">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="team-member">
|
<div class="team-member">
|
||||||
{{ with resources.Get "/images/logos/civicert.png" }}
|
{{ with resources.Get "/images/logos/civicert.png" }}
|
||||||
<img style="max-height: 100px;" src="{{ .RelPermalink }}" alt="CiviCERT">
|
<img style="max-height: 100px;" src="{{ .RelPermalink }}" alt="CiviCERT">
|
||||||
|
|
@ -11,8 +12,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="team-member">
|
<div class="team-member">
|
||||||
{{ with resources.Get "/images/logos/cdr.png" }}
|
{{ with resources.Get "/images/logos/fdroid.webp" }}
|
||||||
<img style="max-height: 100px;" src="{{ .RelPermalink }}" alt="Centre for Digital Resilience">
|
<img style="max-height: 100px;" src="{{ .RelPermalink }}" alt="F-Droid">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -23,14 +24,14 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="team-member">
|
<div class="team-member">
|
||||||
{{ with resources.Get "/images/logos/hetzner.webp" }}
|
{{ with resources.Get "/images/logos/cdr.png" }}
|
||||||
<img style="max-height: 100px;" src="{{ .RelPermalink }}" alt="Hetzner">
|
<img style="max-height: 100px;" src="{{ .RelPermalink }}" alt="Centre for Digital Resilience">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="team-member">
|
<div class="team-member">
|
||||||
{{ with resources.Get "/images/logos/openrightsgroup.png" }}
|
{{ with resources.Get "/images/logos/hetzner.webp" }}
|
||||||
<img style="max-height: 100px;" src="{{ .RelPermalink }}" alt="Open Rights Group">
|
<img style="max-height: 100px;" src="{{ .RelPermalink }}" alt="Hetzner">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -39,3 +40,9 @@
|
||||||
<img style="max-height: 80px;" src="{{ .RelPermalink }}" alt="Nominet Accredited Channel Partner">
|
<img style="max-height: 80px;" src="{{ .RelPermalink }}" alt="Nominet Accredited Channel Partner">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="team-member">
|
||||||
|
{{ with resources.Get "/images/logos/openrightsgroup.png" }}
|
||||||
|
<img style="max-height: 100px;" src="{{ .RelPermalink }}" alt="Open Rights Group">
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
|
|
||||||
8
policies/Justfile
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
update:
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
for file in *.bs; do
|
||||||
|
specname="${file%.bs}"
|
||||||
|
mkdir -p "../static/policies/${specname}/"
|
||||||
|
bikeshed spec "${file}" "../static/policies/${specname}/index.html"
|
||||||
|
done
|
||||||
|
|
||||||
8
policies/biblio.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"EFF-DICE": {
|
||||||
|
"href": "https://www.eff.org/dice",
|
||||||
|
"title": "EFF Dice-Generated Passphrases",
|
||||||
|
"publisher": "Electronic Frontier Foundation",
|
||||||
|
"source": "https://www.eff.org/dice"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
policies/copyright.include
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
© <a href="https://www.sr2.uk/">SR2 Communications Limited</a>.
|
||||||
|
This document is licensed under <a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.
|
||||||
|
<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg" alt="" style="max-width: 1em;max-height:1em;margin-left: .2em;" no-autosize><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg" alt="" style="max-width: 1em;max-height:1em;margin-left: .2em;" no-autosize>
|
||||||
30
policies/header.include
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<title>[TITLE]</title>
|
||||||
|
<style data-fill-with="stylesheet">
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="h-entry">
|
||||||
|
<div class="head">
|
||||||
|
<p style="background-color: #000; padding: 10px; font-size: large; font-weight: bold; color: #fff; float: right;">TLP:CLEAR</p>
|
||||||
|
<img src="https://www.sr2.uk/images/logo.png" alt="SR2 Communications Limited" width="400" style="margin-bottom: 10px;">
|
||||||
|
<h1 id="title" class="p-name no-ref">[TITLE]</h1>
|
||||||
|
<h2 id="subtitle" class="no-num no-toc no-ref">Draft for Approval by Company Directors,
|
||||||
|
<span class="dt-updated"><span class="value-title" title="[CDATE]">[DATE]</span></span>
|
||||||
|
</h2>
|
||||||
|
<div data-fill-with="spec-metadata"></div>
|
||||||
|
<div data-fill-with="warning"></div>
|
||||||
|
<p class='copyright' data-fill-with="copyright"></p>
|
||||||
|
<hr title="Separator for header">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-summary" data-fill-with="abstract"></div>
|
||||||
|
<div data-fill-with="at-risk"></div>
|
||||||
|
|
||||||
|
<nav data-fill-with="table-of-contents" id="toc"></nav>
|
||||||
|
<main>
|
||||||
147
policies/password_auth.bs
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
<h1>Passwords and Authentication Policy</h1>
|
||||||
|
<pre class="metadata">
|
||||||
|
Status: DREAM
|
||||||
|
Local Boilerplate: header yes, copyright yes, defaults yes
|
||||||
|
Boilerplate: status no
|
||||||
|
TR: https://www.sr2.uk/policies/password-auth/
|
||||||
|
Shortname: password-auth
|
||||||
|
Complain About: accidental-2119 yes
|
||||||
|
No Editor: true
|
||||||
|
!Version: 1.0
|
||||||
|
Abstract: A policy defining an effective authentication management procedures when conducting company-related business.
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
# Objective # {#objective}
|
||||||
|
|
||||||
|
This policy defines an effective authentication management procedures when conducting company-related business and
|
||||||
|
includes the:
|
||||||
|
|
||||||
|
* issuing and selection of strong authentication methods and credentials;
|
||||||
|
* protection of secret authentication credentials;
|
||||||
|
* frequency of change in terms of authentication credentials;
|
||||||
|
* reporting of any suspected breach or lost authentication credentials;
|
||||||
|
* use of authentication methods with third party systems (including cloud technology).
|
||||||
|
|
||||||
|
Authentication is a key method of securing our information – choosing weak authentication methods, or failing to keep
|
||||||
|
the authentication credentials secure, places the confidentiality of our data at risk.
|
||||||
|
|
||||||
|
# Scope # {#scope}
|
||||||
|
|
||||||
|
The scope of the policy covers all individuals either employed or contracted to work with or for the company, either
|
||||||
|
in-office or remotely.
|
||||||
|
|
||||||
|
# Definitions # {#definitions}
|
||||||
|
|
||||||
|
: Authentication method
|
||||||
|
:: Any method by which a user may authenticate themselves in order to gain access to a location, data or service, such
|
||||||
|
as text entry (e.g. passwords, passphrases, PINs), biometrics (e.g. fingerprints), etc.
|
||||||
|
: Authentication credentials
|
||||||
|
:: The specific data or information used by a user to authenticate themselves, including but not limited to passwords,
|
||||||
|
passphrases, PINs, and biometric data.
|
||||||
|
: Multi-Factor Authentication (MFA)
|
||||||
|
:: An authentication method that requires the user to provide two or more verification factors to gain access, such as
|
||||||
|
something they know (e.g., password), something they have (e.g., a security token or mobile device), and/or
|
||||||
|
something they are (e.g., biometric data).
|
||||||
|
: Cloud-based system
|
||||||
|
:: A service or platform hosted over the internet that allows users to access data, applications and services remotely.
|
||||||
|
: Password manager
|
||||||
|
:: A software product used for the secure storage of passwords, which must be approved for use, and includes functions
|
||||||
|
for generating strong passwords compliant with this policy.
|
||||||
|
|
||||||
|
# Policy # {#policy}
|
||||||
|
|
||||||
|
Authentication method covers any methods by which a user may authenticate themselves in order to gain access to a
|
||||||
|
location, data or service, such as text entry (e.g. passwords, passphrases, PINs), biometrics (e.g. fingerprints), etc.
|
||||||
|
The company ensures that authentication credentials are kept confidential by:
|
||||||
|
|
||||||
|
- storing authentication credentials in a secure manner;
|
||||||
|
- changing manufacturer default authentication credentials and disabling guest accounts on all equipment;
|
||||||
|
- issuing new users with temporary authentication credentials, which must be changed at first login to a stronger
|
||||||
|
alternative (defined later);
|
||||||
|
- authentication credentials issued to new users are done so in a secure manner (e.g. never in clear text via an email);
|
||||||
|
- changing all multi-user credentials (e.g. for communal equipment) used by an employee in the event that their
|
||||||
|
employment ends;
|
||||||
|
- ensuring that access to user credentials is limited to ICT administrators for the purpose of resetting, revoking or
|
||||||
|
problem resolution – authentication methods may only be reset once the identity of the user has been verified;
|
||||||
|
- locking accounts after 5 failed login attempts in order to dissuade brute-forcing attempts;
|
||||||
|
- training staff in the use of digital password managers, and the risks of storing passwords in any other form (such as
|
||||||
|
a notebook at their workstation, or Post-It note).
|
||||||
|
|
||||||
|
Users must ensure that they do all they can to maintain the confidentiality of their authentication credentials by
|
||||||
|
never:
|
||||||
|
|
||||||
|
- using company authentication credentials for any other account they hold (including personal accounts such as home
|
||||||
|
utilities, email, online shopping services, etc);
|
||||||
|
- having a physical copy of their credentials;
|
||||||
|
- using a non-approved method for password generation;
|
||||||
|
- entering authentication credentials on non-company equipment (for example, home or public access PCs);
|
||||||
|
- revealing authentication credentials to anyone, including line managers, unless relaying information on temporary
|
||||||
|
credentials which are changed immediately upon next login. This includes never
|
||||||
|
sharing authentication credentials with co-workers (e.g. whilst on annual leave);
|
||||||
|
- discussing authentication credentials in front of others.
|
||||||
|
|
||||||
|
## Password Authentication ## {#passwords}
|
||||||
|
|
||||||
|
Many services and policies only allow for password authentication methods, and so they are given a special focus here.
|
||||||
|
Strong passwords MUST be used for authentication. The company defines a strong password as one generated by one of two
|
||||||
|
processes: random string generation by a password manager or using diceware [[!EFF-DICE]].
|
||||||
|
|
||||||
|
Where a password is to be stored in a password manager, it MUST be randomly generated by the password manager with the
|
||||||
|
parameters:
|
||||||
|
|
||||||
|
- having a minimum number of 14 characters in length;
|
||||||
|
- using longer passwords where permitted by the service;
|
||||||
|
- including a mixture of numbers, upper and lower case letters, and special characters.
|
||||||
|
|
||||||
|
Where special characters are not possible due to technical restrictions, the minimum length is 20 characters.
|
||||||
|
|
||||||
|
For the avoidance of doubt, weak passwords must never be used. Weak, text-based authentication credentials generally
|
||||||
|
have one or more of the following characteristics:
|
||||||
|
|
||||||
|
- credential is the same, or partly the same, as the username;
|
||||||
|
- names of family members, friends, or pets are used;
|
||||||
|
- personal information about yourself or family members which can be easily found from social networking sites,
|
||||||
|
including date of birth, phone number, street name, etc.;
|
||||||
|
- consecutive alphanumeric characters or keys on the keyboard, such as ‘abc123’ or ‘qwerty’;
|
||||||
|
- dictionary words including the inclusion of a number or character at the start or end or substituting numbers or
|
||||||
|
punctuation for letters, for example, ‘P@55w0rd’;
|
||||||
|
- a known word from any language (which may not be in a dictionary).
|
||||||
|
|
||||||
|
For passwords that are intended to be memorised, the MUST be generated using diceware. The above restrictions likely
|
||||||
|
will not be met using this method as the intention is to provide a strong password that is easy to remember, and the
|
||||||
|
strength comes from the underlying dice rolls. Any other method of generating a passphrase MUST NOT be used even if it
|
||||||
|
results in one that bears similarity to a diceware-generated passphrase.
|
||||||
|
|
||||||
|
Memorised passphrases generated with diceware SHOULD be used for:
|
||||||
|
|
||||||
|
- end-user device login passphrase;
|
||||||
|
- password manager decryption passphrase.
|
||||||
|
|
||||||
|
## Multi-Factor Authentication ## {#mfa}
|
||||||
|
|
||||||
|
Wherever the option is offered by a given service or piece of software, multi-factor authentication is to be used (e.g.
|
||||||
|
a fingerprint and a passphrase, or a voice sample, PIN and verification SMS).
|
||||||
|
|
||||||
|
Where a hardware token is in use to authenticate to a system without a password, the token itself MUST be secured with
|
||||||
|
a memorised PIN of at least 6 digits.
|
||||||
|
|
||||||
|
## Credentials for Cloud-Based Systems and Online Portals ## {#cloud}
|
||||||
|
|
||||||
|
It is to be remembered that the company makes use of cloud-based technology and online portals, which may not enforce
|
||||||
|
strong authentication credentials. It is therefore up to the individual to ensure a good authentication regime is
|
||||||
|
maintained, which is as strong as that used within the organisation. In line with the company’s "Internet Use
|
||||||
|
Policy", users shall:
|
||||||
|
|
||||||
|
- not create an online account for business purposes without authorisation from a director;
|
||||||
|
- advise a director when there is no longer a need to have the online account in order to ensure that it is
|
||||||
|
removed.
|
||||||
|
|
||||||
|
## Credential Compromise Policy ## {#compromise}
|
||||||
|
|
||||||
|
In the event of a credential compromise, users SHALL take immediate action to secure the account by resetting or
|
||||||
|
invalidating the credentials and report the incident to a director as soon as practical.
|
||||||
|
It is policy that any password compromise event will be shared with CiviCERT members via the MISP platform to allow for
|
||||||
|
shared learning from the incident.
|
||||||
|
Directors will be responsible for determining if a data breach notification is necessary to our clients or to the
|
||||||
|
Information Commissioners Office.
|
||||||
|
|
||||||
61
policies/public_wifi.bs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
<h1>Public WiFi Policy</h1>
|
||||||
|
<pre class="metadata">
|
||||||
|
Status: DREAM
|
||||||
|
Local Boilerplate: header yes, copyright yes
|
||||||
|
Boilerplate: status no
|
||||||
|
TR: https://www.sr2.uk/policies/public-wifi/
|
||||||
|
Shortname: public-wifi
|
||||||
|
Complain About: accidental-2119 yes
|
||||||
|
No Editor: true
|
||||||
|
!Version: 1.0
|
||||||
|
Abstract: A policy governing staff and contractor use of public WiFi networks when accessing company data.
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
# Objective # {#objective}
|
||||||
|
|
||||||
|
The company approves remote working to work-related cloud services and work email accounts, as long as the devices used
|
||||||
|
to access these have been sanctioned by the company. Using public WiFi to conduct business, without the necessary
|
||||||
|
safeguards, places our data at risk of theft. The purpose of this policy is to provide the framework for those
|
||||||
|
safeguards.
|
||||||
|
|
||||||
|
# Scope # {#scope}
|
||||||
|
|
||||||
|
The scope of the policy covers all individuals either employed or contracted to work with, or for, the company, either
|
||||||
|
on a company site or remotely.
|
||||||
|
|
||||||
|
# Definitions # {#definitions}
|
||||||
|
|
||||||
|
: Public WiFi Network
|
||||||
|
:: Any wireless network access provided by a third party, such as hotels, cafes, airports, or public hotspots, that is
|
||||||
|
open to public or unvetted access. For the purpose of this policy, eduroam connections other than those on an SR2
|
||||||
|
managed site are to be considered Public WiFi Networks.
|
||||||
|
: Sanctioned Device
|
||||||
|
:: A device (e.g., laptop, tablet, smartphone) that has been approved and provisioned by the
|
||||||
|
company for business use, with appropriate security configurations and software installed.
|
||||||
|
|
||||||
|
# Policy # {#policy}
|
||||||
|
|
||||||
|
Devices that are not sanctioned by the company, including home PCs or public access PCs, MUST NOT be used to access
|
||||||
|
company cloud services, data, or email accounts.
|
||||||
|
|
||||||
|
Though the company takes every effort to ensure that sanctioned devices are adequately protected, the individual MUST
|
||||||
|
ensure that, before connecting to the Wi-Fi network, the device has:
|
||||||
|
|
||||||
|
- up-to-date antivirus and antispyware software;
|
||||||
|
- a firewall that is activated and configured to company requirements (i.e. the settings have not been changed) since
|
||||||
|
the device was configured;
|
||||||
|
- all software (including the Web browser) is current with automatic updating;
|
||||||
|
- file sharing (e.g. SMB) is switched off.
|
||||||
|
|
||||||
|
For security reasons staff and contractors MUST:
|
||||||
|
|
||||||
|
- consider if mobile phone tethering is available and use this as the first choice;
|
||||||
|
- consider delaying transmission of information until at a secure location;
|
||||||
|
- not follow prompts to update software whilst connected to a public network;
|
||||||
|
- not rely on the encryption provided by the Public WiFi Network (e.g. WPA) to protect company data;
|
||||||
|
- ensure that an end-to-end encrypted connection is established and the user has been trained in setting up
|
||||||
|
such a connection for each service to be used (for the avoidance of doubt, TLS is considered to be end-to-end
|
||||||
|
providing that the certificate presented by the server is validated);
|
||||||
|
- ensure that URLs in Web browsers are showing the correct Web addresses in case a criminal has hijacked the Wireless
|
||||||
|
Access Point and is forwarding traffic to their site;
|
||||||
|
- keep all information secure, including restricting the view of the screen from any unauthorised person(s);
|
||||||
7
static/.well-known/microsoft-identity-association.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"associatedApplications": [
|
||||||
|
{
|
||||||
|
"applicationId": "1eb4853c-f999-4763-85b5-8d9bd0035bc0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
static/cyber-essentials.pdf
Normal file
22
static/helpdesk.asc
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Comment: 1135 3E54 83C7 152B 165C 46A7 9CE7 365E C2E1 4728
|
||||||
|
Comment: SR2 Helpdesk (Shared Mailbox) <contact@sr2.uk>
|
||||||
|
|
||||||
|
xjMEaUqxIBYJKwYBBAHaRw8BAQdAFRCg++SH2sipx7dN977soQzmlAzVM+2f9iKE
|
||||||
|
fFPMjYXNLlNSMiBIZWxwZGVzayAoU2hhcmVkIE1haWxib3gpIDxjb250YWN0QHNy
|
||||||
|
Mi51az7CmQQTFgoAQRYhBBE1PlSDxxUrFlxGp5znNl7C4UcoBQJpSrEgAhsBBQkD
|
||||||
|
wmcABQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEJznNl7C4Ucof3EA/R91
|
||||||
|
WLgYJKg7EI9tVjc1CwBvcsq5i5rV517XBJpvgeVYAQDbcZ/Hd1aH4kNWai7FGwZJ
|
||||||
|
Umam/eHhBbgEUKuMLmtBDM4zBGlKsTMWCSsGAQQB2kcPAQEHQDMsWTxTDvrlK43J
|
||||||
|
1IFU3ncSUCPqfs25kRXEoxYsUmJBwsA1BBgWCgAmFiEEETU+VIPHFSsWXEannOc2
|
||||||
|
XsLhRygFAmlKsTMCGwIFCQPCZwAAgQkQnOc2XsLhRyh2IAQZFgoAHRYhBJSgeWhx
|
||||||
|
n4DES3R4uVQdQ7N7rA5JBQJpSrEzAAoJEFQdQ7N7rA5JDCcA/0hhu5bkHLezhgqH
|
||||||
|
fqYSLmtp2TV5GW1rcZ8SA4TfdT5wAP9d0grZFtrTwqQBQz/v5RzSKhHcSRI9uFZL
|
||||||
|
qXpj3HUsAI54AP9b078TsRtPHsIluPtxPZ0t1JYVWC8A4/ii/q5c+vREyAD+P7Om
|
||||||
|
Bk2VgHtT2yiuCKVbFdle/TOPdU7klutYlzEbzAnOOARpSrFBEgorBgEEAZdVAQUB
|
||||||
|
AQdAi5FmgcXOHwroZxoD/X6tuLzYrdV8KXeKu1I8FMbVrHEDAQgHwn4EGBYKACYW
|
||||||
|
IQQRNT5Ug8cVKxZcRqec5zZewuFHKAUCaUqxQQIbDAUJA8JnAAAKCRCc5zZewuFH
|
||||||
|
KD6JAQD1qISJfiEvrmTCEV97An8jGhcYk22CHzzGgB3vljQHagD/QM6HQsBjDENc
|
||||||
|
KCmNOoaN/Yq6IM2Rc/tkGr/ALdhwggs=
|
||||||
|
=W9Nf
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
BIN
static/images/2026/cyber-essentials.png
Normal file
|
After Width: | Height: | Size: 528 KiB |
BIN
static/images/2026/halow-coverage.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
static/images/2026/halow-powerbank.png
Normal file
|
After Width: | Height: | Size: 6.2 MiB |
BIN
static/images/2026/portal-branding.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
static/images/2026/portal-deltachat.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
static/images/2026/portal-security.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
2281
static/policies/password_auth/index.html
Normal file
2172
static/policies/public_wifi/index.html
Normal file
|
|
@ -1 +1 @@
|
||||||
Subproject commit 1c9895212ec3829b7c94f8042c9d0ba060d93d81
|
Subproject commit 596b4e4810ed300bc8bc84d8b0cf2c8cde8a8582
|
||||||