Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
0afe844df9 | |||
260af1c02d |
@ -1,35 +0,0 @@
|
||||
name: Deploy Hugo site to Prod
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["master"]
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build Container
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
github-server-url: https://gitea.sheep-ghoul.ts.net
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: gitea.sheep-ghoul.ts.net
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build Container
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
tags: gitea.sheep-ghoul.ts.net/sarsoo/sarsooxyz.hugo:latest
|
||||
file: Dockerfile.pub
|
||||
context: .
|
2
.github/workflows/docker.yml
vendored
@ -50,4 +50,4 @@ jobs:
|
||||
version: 1.68.1
|
||||
|
||||
- name: Deploy
|
||||
run: ssh -o StrictHostKeyChecking=no ${{ secrets.TS_SSH }} -t "cd sarsooxyz-hugo/ && docker compose up -d --pull always"
|
||||
run: ssh -o StrictHostKeyChecking=no aj@odb -t "cd sarsooxyz-hugo/ && docker compose up -d --pull always"
|
||||
|
12
.github/workflows/pages.yml
vendored
@ -1,13 +1,13 @@
|
||||
# Sample workflow for building and deploying a Hugo site to GitHub Pages
|
||||
name: Deploy Hugo site to Staging (Pages)
|
||||
|
||||
on: {}
|
||||
# # Runs on pushes targeting the default branch
|
||||
# push:
|
||||
# branches: ["master"]
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["master"]
|
||||
|
||||
# # Allows you to run this workflow manually from the Actions tab
|
||||
# workflow_dispatch:
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
|
@ -1,7 +1,7 @@
|
||||
[sarsoo.xyz](https://sarsoo.xyz)
|
||||
===============
|
||||
|
||||
![ci](https://github.com/sarsoo/sarsooxyz.hugo/actions/workflows/docker.yml/badge.svg)
|
||||
![ci](https://github.com/sarsoo/sarsooxyz.hugo/actions/workflows/pages.yml/badge.svg)
|
||||
|
||||
Hugo static site for [sarsoo.xyz](https://sarsoo.xyz).
|
||||
|
||||
|
@ -1,304 +0,0 @@
|
||||
:root {
|
||||
// --shadow-colour: #828282;
|
||||
--shadow-colour: #3f0aff;
|
||||
}
|
||||
|
||||
p {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.silkscreen-regular,
|
||||
.categories > a,
|
||||
.tags > span > a,
|
||||
.taxonomy-element > a {
|
||||
font-family: "Silkscreen", sans-serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.navigation-title {
|
||||
font-size: 4rem !important;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.about > *,
|
||||
.content header h1,
|
||||
.navigation-title {
|
||||
font-family: "Pirata One", apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, "游ゴシック", "PingFang SC", STXihei, "华文细黑", "Microsoft YaHei", "微软雅黑", SimSun, "宋体", Heiti, "黑体", sans-serif !important;
|
||||
font-weight: 400 !important;
|
||||
font-style: normal !important;
|
||||
}
|
||||
|
||||
.about {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.about > h1 {
|
||||
font-size: 5rem !important;
|
||||
}
|
||||
|
||||
.about > h2 {
|
||||
font-size: 3rem !important;
|
||||
}
|
||||
|
||||
@mixin shadowed {
|
||||
box-shadow: var(--shift) var(--shift) 2px var(--shadow-colour);
|
||||
translate: calc(var(--shift) * -1) calc(var(--shift) * -1);
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
transition: box-shadow, translate;
|
||||
transition-duration: var(--duration);
|
||||
transition-timing-function: ease-in-out;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0px 0px 0px var(--shadow-colour);
|
||||
translate: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shadowed {
|
||||
--shift: 8px;
|
||||
--duration: 0.2s;
|
||||
|
||||
@include shadowed;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////
|
||||
// AVATAR
|
||||
///////////////////////
|
||||
|
||||
.avatar {
|
||||
|
||||
img {
|
||||
--shift: 20px;
|
||||
--transition-time: 1s;
|
||||
|
||||
border-style: solid;
|
||||
border-width: 7px;
|
||||
border-color: black;
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
animation: shift-avatar 3s ease-in-out infinite alternate backwards;
|
||||
|
||||
@keyframes shift-avatar {
|
||||
from {
|
||||
filter: drop-shadow(0px 0px 0px var(--shadow-colour));
|
||||
translate: none;
|
||||
}
|
||||
to {
|
||||
filter: drop-shadow(var(--shift) 8px 1px var(--shadow-colour));
|
||||
translate: calc(var(--shift) * -1) calc(var(--shift) * -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
img:hover {
|
||||
filter: none;
|
||||
translate: none;
|
||||
// animation-play-state: paused;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.avatar img {
|
||||
--shift: 13px;
|
||||
border-width: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// BOX LINK
|
||||
///////////////////////
|
||||
|
||||
.box-link {
|
||||
--shift: 8px;
|
||||
--border-radius: 10px;
|
||||
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 17rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
translate: calc(var(--shift) * -1) calc(var(--shift) * -1);
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
transition: translate;
|
||||
transition-duration: 0.25s;
|
||||
transition-timing-function: ease-in-out;
|
||||
|
||||
animation: shift-image 0.3s backwards;
|
||||
@keyframes shift-image {
|
||||
from { translate: none; }
|
||||
to { translate: calc(var(--shift) * -1) calc(var(--shift) * -1); }
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
border-radius: var(--border-radius);
|
||||
opacity: .5;
|
||||
background-color: black;
|
||||
position: absolute;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
/* object-position:38% 50%; */
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%; height: 100%;
|
||||
overflow: hidden;
|
||||
object-fit: cover;
|
||||
border: 4px solid black;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shift) var(--shift) 1px var(--shadow-colour);
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
transition: box-shadow;
|
||||
transition-duration: 0.25s;
|
||||
transition-timing-function: ease-in-out;
|
||||
animation: shift-shadow 0.3s backwards;
|
||||
@keyframes shift-shadow {
|
||||
from { box-shadow: none; }
|
||||
to { box-shadow: var(--shift) var(--shift) 1px var(--shadow-colour); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
&:hover {
|
||||
translate: none;
|
||||
|
||||
img {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-section {
|
||||
z-index: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: white;
|
||||
text-align: center !important;
|
||||
text-shadow: 3px 3px 5px black;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.center-link-text {
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
text-underline-offset: 3px;
|
||||
text-decoration-skip-ink: auto;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// FOOTER LOGO
|
||||
///////////////////////
|
||||
|
||||
.footer-logo {
|
||||
filter: contrast(100%);
|
||||
transition: filter 1s ease-in-out;
|
||||
}
|
||||
|
||||
.footer-logo:hover {
|
||||
filter: contrast(0%) drop-shadow(7px 7px 5px grey);
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// ART GRID
|
||||
///////////////////////
|
||||
|
||||
.art-grid {
|
||||
--shift: 5px;
|
||||
--duration: 0.2s;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
|
||||
& > figure > img {
|
||||
border: 5px solid black;
|
||||
@include shadowed;
|
||||
}
|
||||
|
||||
& > .art-col-2 {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
& > .art-col-3 {
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
||||
& > .art-row-2 {
|
||||
grid-row: span 2;
|
||||
}
|
||||
|
||||
& > .art-row-3 {
|
||||
grid-row: span 3;
|
||||
}
|
||||
}
|
||||
|
||||
.art-home-grid {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.dev-grid {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.dev-grid-col-2 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.dev-grid-col-3 {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.art-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.art-home-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.dev-grid {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.youtube-embed {
|
||||
iframe {
|
||||
--shift: 8px;
|
||||
--duration: .2s;
|
||||
|
||||
width: 100%;
|
||||
aspect-ratio: 16/9;
|
||||
border-radius: 20px;
|
||||
|
||||
border: 5px solid black;
|
||||
|
||||
@include shadowed;
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
html {
|
||||
hanging-punctuation: first last;
|
||||
}
|
||||
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 75ch;
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
@media screen and (prefers-reduced-motion: no-preference) {
|
||||
:has(:target) {
|
||||
scroll-behavior: smooth;
|
||||
scroll-padding-top: 3rem;
|
||||
}
|
||||
}
|
31
config.toml
@ -1,6 +1,6 @@
|
||||
baseURL = 'https://sarsoo.xyz/'
|
||||
languageCode = 'en-gb'
|
||||
title = 'sarsoo'
|
||||
title = 'sarsoo.xyz'
|
||||
theme = 'hugo-coder'
|
||||
|
||||
paginate = 40
|
||||
@ -24,11 +24,9 @@ paginate = 40
|
||||
avatarurl = "images/avatar.jpg"
|
||||
hideColorSchemeToggle = true
|
||||
|
||||
customSCSS = ["scss/reset.scss", "scss/main.scss"]
|
||||
|
||||
[[params.social]]
|
||||
name = "Mastodon"
|
||||
icon = "fa-brands fa-mastodon fa-2x"
|
||||
icon = "fa fa-mastodon fa-2x"
|
||||
weight = 1
|
||||
url = "https://fosstodon.org/@sarsoo/"
|
||||
[[params.social]]
|
||||
@ -53,31 +51,36 @@ paginate = 40
|
||||
url = "https://www.linkedin.com/in/andypack/"
|
||||
|
||||
[[menu.main]]
|
||||
name = "Dev & Engineering"
|
||||
name = "Mixonomer"
|
||||
weight = 1
|
||||
url = "mixonomer/"
|
||||
|
||||
[[menu.main]]
|
||||
name = "Selector"
|
||||
weight = 2
|
||||
url = "selector/"
|
||||
|
||||
[[menu.main]]
|
||||
name = "Dev & Engineering"
|
||||
weight = 3
|
||||
url = "dev-engineering/"
|
||||
|
||||
[[menu.main]]
|
||||
name = "Music"
|
||||
weight = 2
|
||||
weight = 4
|
||||
url = "music/"
|
||||
|
||||
[[menu.main]]
|
||||
name = "Art"
|
||||
weight = 3
|
||||
weight = 5
|
||||
url = "art/"
|
||||
|
||||
[[menu.main]]
|
||||
name = "Posts"
|
||||
weight = 4
|
||||
weight = 6
|
||||
url = "posts/"
|
||||
|
||||
[[menu.main]]
|
||||
name = "Tags"
|
||||
weight = 5
|
||||
url = "tags/"
|
||||
|
||||
[[menu.main]]
|
||||
name = "About"
|
||||
weight = 6
|
||||
weight = 7
|
||||
url = "about/"
|
@ -5,19 +5,19 @@ date: 2020-12-25T00:04:40+00:00
|
||||
|
||||
{{% avatar "/images/holo-avatar.jpg" %}}
|
||||
|
||||
<span style="font-size: 30px;">🇬🇧</span> software engineer with experience in __fintech__.
|
||||
UK-based software engineer with experience in __fintech__.
|
||||
|
||||
[__Award-winning__](/dev-engineering/#awards), __first-class__ __masters__ [electronic engineering](/dev-engineering) graduate & previous __Disney__ intern
|
||||
|
||||
I have 8 years experience programming working from [embedded systems](/posts/iot) to [holoportation](/holo) and [full-stack web-dev](/mixonomer).
|
||||
|
||||
Check out my side projects in [__Python__](/tags/python), [__Javascript__](/tags/javascript/), [__Typescript__](/selector), [__C#__](/tags/c%23/) and [__Rust__](/tags/rust/)
|
||||
Check out my side projects in [__Python__](/mixonomer), [__Javascript__](/mixonomer), [__Typescript__](/selector), [__C#__](/selector) and [__Rust__](/posts/draught)
|
||||
|
||||
I also have 6 years experience with __Linux__ for both desktop and server. I use [__Terraform__](/tags/terraform/) and [__Ansible__](/tags/ansible/) to manage my infrastructure 'on-prem' and in the cloud, I have experience with __Jenkins__ and __Github Actions__ for __CI/CD__ pipelines
|
||||
I also have 6 years experience with __Linux__ for both desktop and server. I use __Terraform__ and __Ansible__ to manage my infrastructure 'on-prem' and in the cloud, I have experience with __Jenkins__ and __Github Actions__ for __CI/CD__ pipelines
|
||||
|
||||
---
|
||||
|
||||
[I draw sometimes too](/art/)
|
||||
[I draw sometimes too](https://www.instagram.com/pack_it_in_/)
|
||||
|
||||
---
|
||||
|
||||
|
@ -2,12 +2,7 @@
|
||||
title: 'Art'
|
||||
---
|
||||
|
||||
<div class="art-home-grid">
|
||||
|
||||
{{% image-box-link src="/art/animal/gorilla-baby.jpg" href="/art/animal" title="Animals" %}}
|
||||
{{% image-box-link src="/art/portrait/updown.jpg" href="/art/portrait" title="Portraits" %}}
|
||||
{{% image-box-link src="/art/portrait/skull.jpg" href="/art/portrait" title="Portraits" %}}
|
||||
{{% image-box-link src="/art/sketchbook/flowers.jpg" href="/art/sketchbook" title="Sketchbook" %}}
|
||||
{{% image-box-link src="/art/life/brown-white.jpg" href="/art/life" title="Life" %}}
|
||||
{{% image-box-link src="/art/portrait/skull.jpg" href="/art/digital" title="Digital" %}}
|
||||
|
||||
</div>
|
Before Width: | Height: | Size: 934 KiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 897 KiB |
Before Width: | Height: | Size: 915 KiB |
Before Width: | Height: | Size: 689 KiB After Width: | Height: | Size: 1.4 MiB |
@ -2,14 +2,8 @@
|
||||
title: 'Animals'
|
||||
---
|
||||
|
||||
{{< art-grid >}}
|
||||
|
||||
{{< figure src="zu.jpg" >}}
|
||||
{{< figure src="zu-charcoal.jpg" class="art-col-2" >}}
|
||||
{{< figure src="orangutan.jpg" >}}
|
||||
{{< figure src="gorilla-baby.jpg" >}}
|
||||
{{< figure src="dali.png" >}}
|
||||
{{< figure src="chimp.jpg" >}}
|
||||
{{< figure src="chimp.png" >}}
|
||||
|
||||
{{< /art-grid >}}
|
||||
{{< figure src="zu.jpg" >}}
|
||||
{{< figure src="zu-charcoal.jpg" >}}
|
||||
{{< figure src="orangutan.jpg" >}}
|
||||
{{< figure src="gorilla-baby.jpg" >}}
|
||||
{{< figure src="chimp.jpg" >}}
|
||||
|
Before Width: | Height: | Size: 529 KiB After Width: | Height: | Size: 2.1 MiB |
Before Width: | Height: | Size: 715 KiB After Width: | Height: | Size: 2.6 MiB |
Before Width: | Height: | Size: 868 KiB |
@ -1,12 +0,0 @@
|
||||
---
|
||||
title: 'Digital'
|
||||
---
|
||||
|
||||
{{< art-grid >}}
|
||||
|
||||
{{< figure src="/art/portrait/skull.jpg" >}}
|
||||
{{< figure src="tilt.png" >}}
|
||||
{{< figure src="/images/holo-avatar.jpg" >}}
|
||||
{{< figure src="arms-up.png" >}}
|
||||
|
||||
{{< /art-grid >}}
|
Before Width: | Height: | Size: 818 KiB |
Before Width: | Height: | Size: 528 KiB After Width: | Height: | Size: 1019 KiB |
Before Width: | Height: | Size: 693 KiB |
Before Width: | Height: | Size: 762 KiB After Width: | Height: | Size: 5.3 MiB |
Before Width: | Height: | Size: 1011 KiB After Width: | Height: | Size: 894 KiB |
@ -2,13 +2,7 @@
|
||||
title: 'Life'
|
||||
---
|
||||
|
||||
{{< art-grid >}}
|
||||
|
||||
{{< figure src="bikini.jpg" >}}
|
||||
{{< figure src="brown-white.jpg" class="art-col-2" >}}
|
||||
{{< figure src="grey.jpg" >}}
|
||||
{{< figure src="red-blue.jpg" >}}
|
||||
{{< figure src="blue.png" >}}
|
||||
{{< figure src="ink-pencil.png" >}}
|
||||
|
||||
{{< /art-grid >}}
|
||||
{{< figure src="bikini.jpg" >}}
|
||||
{{< figure src="brown-white.jpg" >}}
|
||||
{{< figure src="grey.jpg" >}}
|
||||
{{< figure src="red-blue.jpg" >}}
|
||||
|
Before Width: | Height: | Size: 839 KiB |
Before Width: | Height: | Size: 786 KiB After Width: | Height: | Size: 4.5 MiB |
Before Width: | Height: | Size: 733 KiB After Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 823 KiB After Width: | Height: | Size: 1.2 MiB |
@ -2,17 +2,10 @@
|
||||
title: 'Portrait'
|
||||
---
|
||||
|
||||
{{< art-grid >}}
|
||||
|
||||
{{< figure src="freddie.jpg" >}}
|
||||
{{< figure src="girlskull.jpg" >}}
|
||||
{{< figure src="head-back.jpg" >}}
|
||||
{{< figure src="self.jpg" >}}
|
||||
{{< figure src="/art/sketchbook/line-face.jpg" >}}
|
||||
{{< figure src="/art/sketchbook/bran.jpg" >}}
|
||||
{{< figure src="/art/sketchbook/mushroom.jpg" >}}
|
||||
{{< figure src="skull.jpg" >}}
|
||||
{{< figure src="tyler.jpg" >}}
|
||||
{{< figure src="updown.jpg" >}}
|
||||
|
||||
{{< /art-grid >}}
|
||||
{{< figure src="freddie.jpg" >}}
|
||||
{{< figure src="girlskull.jpg" >}}
|
||||
{{< figure src="head-back.jpg" >}}
|
||||
{{< figure src="self.jpg" >}}
|
||||
{{< figure src="skull.jpg" >}}
|
||||
{{< figure src="tyler.jpg" >}}
|
||||
{{< figure src="updown.jpg" >}}
|
||||
|
Before Width: | Height: | Size: 593 KiB After Width: | Height: | Size: 1.8 MiB |
Before Width: | Height: | Size: 817 KiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 828 KiB After Width: | Height: | Size: 2.2 MiB |
Before Width: | Height: | Size: 731 KiB After Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 837 KiB After Width: | Height: | Size: 1.6 MiB |
Before Width: | Height: | Size: 596 KiB After Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 916 KiB After Width: | Height: | Size: 1.5 MiB |
@ -2,20 +2,13 @@
|
||||
title: 'Sketchbook'
|
||||
---
|
||||
|
||||
{{< art-grid >}}
|
||||
|
||||
{{< figure src="blindfold.jpg" >}}
|
||||
{{< figure src="bran.jpg" >}}
|
||||
{{< figure src="buildings.jpg" >}}
|
||||
{{< figure src="flowers.jpg" >}}
|
||||
{{< figure src="grey.jpg" >}}
|
||||
{{< figure src="hand-muscle.jpg" >}}
|
||||
{{< figure src="ok.png" >}}
|
||||
{{< figure src="music.png" class="art-col-2" >}}
|
||||
{{< figure src="line-face.jpg" >}}
|
||||
{{< figure src="mushroom.jpg" >}}
|
||||
{{< figure src="kawagawa.png" >}}
|
||||
{{< figure src="smoke.jpg" >}}
|
||||
{{< figure src="spindly.jpg" >}}
|
||||
|
||||
{{< /art-grid >}}
|
||||
{{< figure src="blindfold.jpg" >}}
|
||||
{{< figure src="bran.jpg" >}}
|
||||
{{< figure src="buildings.jpg" >}}
|
||||
{{< figure src="flowers.jpg" >}}
|
||||
{{< figure src="grey.jpg" >}}
|
||||
{{< figure src="hand-muscle.jpg" >}}
|
||||
{{< figure src="line-face.jpg" >}}
|
||||
{{< figure src="mushroom.jpg" >}}
|
||||
{{< figure src="smoke.jpg" >}}
|
||||
{{< figure src="spindly.jpg" >}}
|
||||
|
Before Width: | Height: | Size: 946 KiB |
Before Width: | Height: | Size: 888 KiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 799 KiB After Width: | Height: | Size: 1.6 MiB |
Before Width: | Height: | Size: 609 KiB |
Before Width: | Height: | Size: 688 KiB |
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.8 MiB |
Before Width: | Height: | Size: 758 KiB After Width: | Height: | Size: 3.6 MiB |
@ -6,17 +6,11 @@ aliases:
|
||||
- /dev
|
||||
---
|
||||
|
||||
[![Gitea](https://img.shields.io/badge/Gitea-34495E?style=for-the-badge&logo=gitea&logoColor=5D9425)](https://git.sarsoo.xyz/sarsoo/-/packages)
|
||||
[![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](https://git.sarsoo.xyz/sarsoo/-/packages?q=&type=pypi)
|
||||
[![Rust](https://img.shields.io/badge/rust-%23000000.svg?style=for-the-badge&logo=rust&logoColor=white)](https://git.sarsoo.xyz/sarsoo/-/packages?q=&type=cargo)
|
||||
[![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)](https://git.sarsoo.xyz/sarsoo/-/packages?q=&type=container)
|
||||
[![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://git.sarsoo.xyz/sarsoo/-/packages?q=&type=helm)
|
||||
{{% image-box-link src="/posts/visual-search/mapSurfaceWithMax2.png" href="/posts/visual-search" title="Visual Search" caption="MATLAB" %}}
|
||||
|
||||
<div class="dev-grid dev-grid-col-2">
|
||||
{{% image-box-link src="/posts/mixonomer/PlaylistExample.png" href="/posts/mixonomer" title="Mixonomer" caption="Python + React" %}}
|
||||
{{% image-box-link src="/posts/lpss/hood_m_gram.png" href="/posts/lpss" title="Speech Synthesiser" caption="MATLAB" %}}
|
||||
|
||||
{{% image-box-link src="/posts/selector/dashboard.png" href="/posts/selector/" title="Selector" caption="C# + TypeScript + Vue.js" %}}
|
||||
</div>
|
||||
{{% image-box-link src="/posts/markov/StateTopology.png" href="/posts/markov" title="Hidden Markov Models" caption="MATLAB" %}}
|
||||
|
||||
# [Infrastructure]({{< relref "infra" >}})
|
||||
|
||||
@ -28,13 +22,7 @@ Basically, Terraform creates and destroys infrastructure, Ansible manages the OS
|
||||
|
||||
[Read More]({{< relref "infra" >}})
|
||||
|
||||
<div class="dev-grid dev-grid-col-3">
|
||||
{{% image-box-link src="/posts/visual-search/mapSurfaceWithMax2.png" href="/posts/visual-search" title="Visual Search" caption="MATLAB" %}}
|
||||
|
||||
{{% image-box-link src="/posts/lpss/hood_m_gram.png" href="/posts/lpss" title="Speech Synth" caption="MATLAB" %}}
|
||||
|
||||
{{% image-box-link src="/posts/markov/StateTopology.png" href="/posts/markov" title="Hidden Markov Models" caption="Python + Numpy" %}}
|
||||
</div>
|
||||
---
|
||||
|
||||
# [Holoportation](/holo)
|
||||
|
||||
@ -49,17 +37,13 @@ The holograms are captured in the form of a __point cloud__, a cluster of colour
|
||||
|
||||
My undergraduate dissertation documented extending the [__LiveScan3D__](https://github.com/MarekKowalski/LiveScan3D) holoportation platform to allow multi-source streaming. The existing capabilities allowed a single scene to be captured in real-time and streamed elsewhere for viewing, multi-source allows multiple independent scenes to be received and composited at the same time (a many-to-one system).
|
||||
|
||||
{{< youtube id=NP0aVjuk5fU autoplay=true controls=false loop=true class=youtube-embed >}}
|
||||
{{< youtube NP0aVjuk5fU >}}
|
||||
|
||||
[Read More](/holo)
|
||||
|
||||
<div class="dev-grid dev-grid-col-3">
|
||||
{{% image-box-link src="/posts/dnstp/server.png" href="/posts/dnstp" title="dnstp" caption="Rust" %}}
|
||||
|
||||
{{% image-box-link src="/posts/draught/checkers-board.png" href="/posts/draught" title="Draught" caption="Rust + Js" %}}
|
||||
|
||||
{{% image-box-link src="/posts/game-of-life/gameoflife1.png" href="/posts/game-of-life" title="Game of Life" caption="Rust + Js" %}}
|
||||
</div>
|
||||
|
||||
# [Mixonomer](/mixonomer)
|
||||
|
||||
@ -82,7 +66,7 @@ The system is now deployed with a fully serverless architecture.
|
||||
|
||||
[Try It Out](https://mixonomer.sarsoo.xyz/)
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/Mixonomer)
|
||||
[Source Code](https://github.com/Sarsoo/Mixonomer)
|
||||
|
||||
---
|
||||
|
||||
@ -99,7 +83,7 @@ A __Spotify__ listening agent which watches what you listen to and presents rela
|
||||
|
||||
[Try It Out](https://selector.sarsoo.xyz/)
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/Selector)
|
||||
[Source Code](https://github.com/Sarsoo/Selector)
|
||||
|
||||
---
|
||||
|
||||
@ -135,13 +119,11 @@ Throughout my studies I found myself particularly interested in the signal proce
|
||||
|
||||
[Posts](/posts)
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo?tab=repositories&q=coursework)
|
||||
[Coursework Code](https://github.com/Sarsoo?tab=repositories&q=coursework)
|
||||
|
||||
---
|
||||
|
||||
<!--- 2016 for coding, 2018 for Linux --->
|
||||
|
||||
I've been coding for 8 years and I now work as a software engineer in fintech. Day-to-day this is in [__C#__](/holo/) and [__TypeScript__](/mixonomer) but I also like working with [__Python__](/mixonomer) and [__Rust__](https://github.com/Sarsoo?tab=repositories&q=&type=&language=rust&sort=). I keep all of my projects on [![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](http://github.com/sarsoo).
|
||||
I've been coding for 8 years and I now work as a software engineer in fintech. Day-to-day this is in [__C#__](/holo/) and [__TypeScript__](/mixonomer) but I also like working with [__Python__](/mixonomer) and [__Rust__](https://github.com/Sarsoo?tab=repositories&q=&type=&language=rust&sort=). I keep all of my projects on [__GitHub__](http://github.com/sarsoo).
|
||||
|
||||
Alongside development I also enjoy working on infrastructure, I have 5 years experience using __Linux__ and managing networks. I have experience working with cloud technologies – from [__virtual machines__](/holo), [__web server PaaS__](/mixonomer) and [__serverless functions__](/mixonomer) to [__NoSQL__](/mixonomer), Big Data SQL and [__pub/sub messaging__](/mixonomer). Much of this experience was gained during my [__Mixonomer__](/mixonomer) project and during my __Disney__ internship. As part of my [dissertation](/holo#research), I used a global cluster of virtual machines as an environment to measure and experiment with holographic video QoS over long distances.
|
||||
|
||||
|
Before Width: | Height: | Size: 68 KiB |
@ -1,129 +0,0 @@
|
||||
---
|
||||
title: "dnstp: Transmitting Arbitrary Data With DNS"
|
||||
date: 2024-10-13T08:26:40+00:00
|
||||
tags:
|
||||
- Rust
|
||||
- Networking
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
![Build Binaries](https://github.com/Sarsoo/dnstp/actions/workflows/build.yml/badge.svg)
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/dnstp)
|
||||
|
||||
[![Rust](https://img.shields.io/badge/rust-%23000000.svg?style=for-the-badge&logo=rust&logoColor=white)](https://git.sarsoo.xyz/sarsoo/-/packages/cargo/dnstplib)
|
||||
[![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)](https://git.sarsoo.xyz/sarsoo/-/packages/container/dnstp)
|
||||
|
||||
[I have written previously about my Rust projects](/tags/rust). However, these have always been in WebAssembly so they are consumed from a web browser. I wanted to play with native Rust code and some if its highly regarded multi-threading features.
|
||||
|
||||
Something I also enjoy, when working with low-level languages, is bitwise operations. These two led me to the desire to do a native Rust project. I was aware of the concept of [DNS tunneling](https://www.zenarmor.com/docs/network-security-tutorials/what-is-dns-tunneling#what-are-the-dns-tunneling-techniques), I think I heard the idea described on [Security This Week with Carl Franklin](https://securitythisweek.com/).
|
||||
|
||||
So that was the inspiration for a project - build some sort of DNS tunneling apparatus, largely to have a toy project that I could fiddle with.
|
||||
|
||||
# spec
|
||||
|
||||
There were a few things that would need to be considered when building something like this:
|
||||
|
||||
1. How much like normal DNS traffic would this tool appear to be
|
||||
2. Is there some sort of DNS library for Rust or would I need to write one
|
||||
3. What would this protocol look like on top of DNS
|
||||
4. How would the data be secured (given that bog-standard DNS is unencrypted)
|
||||
|
||||
# surreptitious
|
||||
|
||||
I decided that the traffic should be standard DNS messages, this was so that any DNS proxies in between would be able to understand the messages and forward them on. The idea is that a hypothetical `dnstp` server wouldn't need to be sent to directly, but could be reached regardless of what path the DNS messages would take.
|
||||
|
||||
In the past, I have had NAT rules on my router that redirected all DNS traffic from the LAN to itself so that it could do the requisite adblocking and upstream forwarding. This is handy for IoT devices like Chromecasts. _As an aside, this will likely be less of a solution as DNS-over-HTTP(s) has become a standard_.
|
||||
|
||||
Because of this possibility that the DNS messages will not go straight from client to server but could be proxied and processed through routers and DNS servers in between, the messages should conform to DNS standards.
|
||||
|
||||
# protocol
|
||||
|
||||
## cryptography + handshakes
|
||||
|
||||
[Docs for the crypto module of `dnstp`](https://sarsoo.github.io/dnstp/dnstplib/crypto/index.html)
|
||||
|
||||
In order to protect the data, it should be encrypted during transmission. The client and server need to be able to agree on a unique, ephemeral key without transmitting it over the insecure channel. This is done using a [key agreement protocol](https://en.wikipedia.org/wiki/Key-agreement_protocol), a specialisation of [key exchange protocols](https://en.wikipedia.org/wiki/Key_exchange). This has the classic advantage of using the more expensive asymmetric cryptography initially to agree on a private but shared symmetric key which is less expensive to use from then on.
|
||||
|
||||
For this implementation, the [Elliptic-curve Diffie–Hellman](https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman) or ECDH algorithm was used, specifically the [p256](https://docs.rs/p256/latest/p256/) crate.
|
||||
|
||||
The protocol for performing this key exchange goes a bit like this. The client generates an ECDH asymmetric pair. It formulates a DNS request in the form:
|
||||
|
||||
```
|
||||
Name Request 1:
|
||||
Record type = A,
|
||||
Hostname = static.CONFIGURED_BASENAME (e.g static.sarsoo.xyz)
|
||||
Name Request 2:
|
||||
Record type = A,
|
||||
Hostname = base_64(CLIENT_PUBLIC_KEY).CONFIGURED_BASENAME
|
||||
```
|
||||
|
||||
When the server identifies that the message is a client handshake request by the hostname of the first request, it generates its own ECDH key pair. The response it sends back to the client is as such:
|
||||
|
||||
```
|
||||
Name Response 1:
|
||||
Record type = A,
|
||||
IP = 127.0.0.1
|
||||
Name Response 2:
|
||||
Record type = CNAME,
|
||||
Hostname = base_64(SERVER_PUBLIC_KEY).CONFIGURED_BASENAME
|
||||
```
|
||||
|
||||
With this response, each side now has the other's public key. With this and their own private key, the shared secret can be divined independently. The server keeps a map of its known clients using their public key as an identifier. It stores its shared secret alongside this key.
|
||||
|
||||
## data transmission
|
||||
|
||||
With this security context set, we can now send data over the insecure channel. The general idea is that the components of each data transfer including the client's public key, the encrypted data and the nonce used during encryption are sent as separate DNS questions.
|
||||
|
||||
That looks like this for an upload value with a corresponding key:
|
||||
|
||||
```
|
||||
Name Request 1:
|
||||
Record type = A,
|
||||
Hostname = base_64(CLIENT_PUBLIC_KEY).CONFIGURED_BASENAME
|
||||
Name Request 2:
|
||||
Record type = A,
|
||||
Hostname = base_64(ENCRYPTED_DESCRIPTIVE_KEY)
|
||||
Name Request 3:
|
||||
Record type = A,
|
||||
Hostname = base_64(ENCRYPTED_VALUE)
|
||||
Name Request 4:
|
||||
Record type = A,
|
||||
Hostname = base_64(ENCRYPTION_NONCE)
|
||||
```
|
||||
|
||||
Where the `ENCRYPTED_DESCRIPTIVE_KEY` question can be omitted for a description-less value.
|
||||
|
||||
{{< figure src="client.png" caption="The client handshakes with the server and then encrypts and transmits the data" alt="client sends the data to the server" >}}
|
||||
|
||||
# dns lib
|
||||
|
||||
[Docs for the DNS message module of `dnstp`](https://sarsoo.github.io/dnstp/dnstplib/message/index.html)
|
||||
|
||||
The first task when I started the project was to work out how to generate and serialise DNS messages. I had a look for an existing crate that could help with this but wasn't satisfied with what I could find, so I decided to write my own. This was a lot of fun because, as I mentioned previously, I love working with bitwise operations. Working with the bit flags of the DNS header was great.
|
||||
|
||||
{{< figure src="server.png" caption="The server receives and completes the handshake before decrypting the follow-up data" alt="server receives data from client" >}}
|
||||
|
||||
# mvp
|
||||
|
||||
So far, the upload feature is working, clients are able to encrypt and transmit data to the server which logs out the unencrypted data. In a hypothetical red team scenario this would be for exfiltration.
|
||||
|
||||
The next step would be downloading data from a server. Ideally, this could be text or files, `dnstp` could be used to download secondary binaries to run.
|
||||
|
||||
Once bi-directional transfers are working, the system could be used for command & control (C2) in a red team context. The client could query the server for commands including downloading and running commands and other binaries.
|
||||
|
||||
# limitations
|
||||
|
||||
The project is very much an MVP at this point; as a toy project, there hasn't been thorough planning of bigger ideas such as session management or error correction.
|
||||
|
||||
There is no retry handling which isn't ideal over the UDP protocol of DNS. There are limits to the lengths of DNS hostnames and of UDP packets in general, how would `dnstp` handle splitting larger data blobs for transmission and do error correction?
|
||||
|
||||
# takeaways
|
||||
|
||||
Working with low-level native Rust was very rewarding, the memory safety and threading systems are pretty ergonomic and make life easy. I think I'll find myself coming back to the project to work on some of the missing features I described.
|
||||
|
||||
[Check out the source code ![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/dnstp)
|
||||
|
||||
[Check out the rust crate ![Rust](https://img.shields.io/badge/rust-%23000000.svg?style=for-the-badge&logo=rust&logoColor=white)](https://git.sarsoo.xyz/sarsoo/-/packages/cargo/dnstplib)
|
||||
|
||||
[Check out the server container ![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)](https://git.sarsoo.xyz/sarsoo/-/packages/container/dnstp)
|
Before Width: | Height: | Size: 331 KiB |
@ -2,11 +2,6 @@
|
||||
title: "Draught: Rust WASM Game With AI Player"
|
||||
date: 2021-07-13T14:02:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Rust
|
||||
- WASM
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
![ci](https://github.com/sarsoo/draught/actions/workflows/test.yml/badge.svg)
|
||||
@ -47,6 +42,6 @@ In order to make the game more fun to play, I introduced a further parameter, th
|
||||
|
||||
Ultimately, I was pretty happy with the project and it can now be used as a testbed for trying out new Rust, WASM and Javascript skills that I learn.
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/sarsoo/draught)
|
||||
[GitHub Repo](https://github.com/sarsoo/draught)
|
||||
|
||||
# [Try it out!](https://draught.sarsoo.xyz/)
|
@ -3,10 +3,6 @@ title: "Electric Vehicle Scheduler"
|
||||
description: "Writing a fullstack electric vehicle parking spot scheduler system"
|
||||
date: 2019-01-14T14:35:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Python
|
||||
- Uni
|
||||
- Cloud
|
||||
---
|
||||
|
||||
As part of my third-year group project, I wrote and deployed a cloud-based electric vehicle scheduling system. The target use case was for public or internally accessible electric vehicle charging spots. Leaving a car in the spot past when it has charged stops other people for using it, the idea was to allow people to book slots for spaces instead.
|
||||
@ -17,7 +13,7 @@ The project was divided into three areas of concern:
|
||||
- A mobile app for requesting slots and receiving notifications
|
||||
- A cloud-based back end service for hosting the application's logic
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/electric-vehicle-scheduler)
|
||||
[Github Repo](https://github.com/Sarsoo/electric-vehicle-scheduler)
|
||||
|
||||
{{< figure src="cloud-structure.png" alt="cloud-structure" >}}
|
||||
|
||||
|
@ -2,11 +2,6 @@
|
||||
title: "Game of Life: Rust WASM Exercise"
|
||||
date: 2021-06-26T14:14:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Rust
|
||||
- WASM
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
![ci](https://github.com/sarsoo/game-of-life/actions/workflows/test.yml/badge.svg)
|
||||
@ -17,6 +12,6 @@ One of my first exposures to Rust WebAssembly was following the Rust + WASM Book
|
||||
|
||||
This would prove a useful base from which to develop my [checkers implementation](/posts/draught).
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/sarsoo/game-of-life)
|
||||
[GitHub Repo](https://github.com/sarsoo/game-of-life)
|
||||
|
||||
[Try it out!](https://life.sarsoo.xyz/)
|
@ -4,13 +4,6 @@ date: 2021-01-19T21:49:40+00:00
|
||||
draft: false
|
||||
aliases:
|
||||
- /holo
|
||||
tags:
|
||||
- Uni
|
||||
- C#
|
||||
- C++
|
||||
- Cloud
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
[LiveScan3D](https://github.com/MarekKowalski/LiveScan3D) is a holographic teleportation or _holoportation_ platform. The app has a client-server model for streaming 3D or _volumetric_ video over the internet. It was written in 2015 by a pair of academics at the Warsaw University of Technology, [Marek Kowalski](http://home.elka.pw.edu.pl/~mkowals6/) and [Jacek Naruniec](http://home.elka.pw.edu.pl/~jnarunie/).
|
||||
@ -42,7 +35,7 @@ My undergraduate dissertation was tasked with extending the original software to
|
||||
|
||||
The development works by including an ID to indicate what source a frame of footage represents.
|
||||
|
||||
{{< youtube id=NP0aVjuk5fU autoplay=true controls=false loop=true class=youtube-embed >}}
|
||||
{{< youtube NP0aVjuk5fU >}}
|
||||
|
||||
###### A couple of recorded sources operating in the virtual space. A third live one is connected part way through
|
||||
|
||||
|
@ -2,11 +2,9 @@
|
||||
title: "Wordpress → Hugo"
|
||||
date: 2022-09-20T07:43:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Cloud
|
||||
---
|
||||
|
||||
![ci](https://github.com/sarsoo/sarsooxyz.hugo/actions/workflows/docker.yml/badge.svg)
|
||||
![ci](https://github.com/sarsoo/sarsooxyz.hugo/actions/workflows/pages.yml/badge.svg)
|
||||
|
||||
My personal website (this one) has been a Wordpress instance for several years. This was a great exercise in running a public facing, static-ish site as well as managing Wordpress itself but it did demonstrate it's drawbacks. Wordpress has some real strengths; it doesn't have a crazy learning curve, it has a nice WYSIWYG editor and it's flexible.
|
||||
|
||||
@ -20,4 +18,4 @@ When it comes to deployment, my first instinct was to use GitHub Pages. The stat
|
||||
|
||||
So, for now, I am hosting the site on my public VPS. This means it's not coming from a crazy large CDN but a single server which could make it a bit slower. I hope that the increased speed from serving only static files should help there. As I said, though, there are so many options to host static sites from GitHub Pages to public facing S3 buckets it could be fun to revisit this. I have left the GitHub Pages workflow in place as a [beta](https://new.sarsoo.xyz) of the site, I push new changes to production with Ansible at the moment.
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/sarsooxyz-hugo)
|
||||
[GitHub Repo](https://github.com/Sarsoo/sarsooxyz-hugo)
|
@ -1,14 +1,6 @@
|
||||
---
|
||||
title: "Terraform + Ansible + Docker: Killer Reproducible & Portable Infrastructure"
|
||||
date: 2023-04-11T21:26:40+00:00
|
||||
tags:
|
||||
- DevOps
|
||||
- Terraform
|
||||
- Ansible
|
||||
- Cloud
|
||||
- Networking
|
||||
categories:
|
||||
- Homelab
|
||||
---
|
||||
|
||||
I have been playing with Ansible & Terraform to manage my infrastructure since early 2022. Both of these technologies have a bit of a learning curve, but in the last few months, I have found the pattern to allow me to expand the services that I run and how comfortable I am with how stable they are.
|
||||
|
@ -2,15 +2,10 @@
|
||||
title: "IoT Coursework"
|
||||
date: 2020-11-24T15:03:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Uni
|
||||
- C
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
My post-grad internet of things coursework concerned coding a smart sensor using a Contiki-based board. Written in C, the software collected light readings into a buffer before processing these data groups using symbolic aggregate approximation or SAX. This piece achieved 95%.
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/IoT-Labs)
|
||||
[GitHub Repo](https://github.com/Sarsoo/IoT-Labs)
|
||||
|
||||
[Read the report here.](report.pdf)
|
@ -2,15 +2,9 @@
|
||||
title: "Listening Engineering: ML + Spotify + Last.fm"
|
||||
date: 2021-02-20T12:22:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Python
|
||||
- ML
|
||||
- Music
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/listening-analysis)
|
||||
[Source Code](https://github.com/Sarsoo/listening-analysis)
|
||||
|
||||
As my [Music Tools](https://sarsoo.xyz/music-tools/) project progressed, I found myself with a cloud environment and a growing dataset of my listening habits to explore. __Spotify__ provides audio features for all of the tracks on its service. These features describe qualities about the track such as how instrumental it is, how much energy it has. I wanted to investigate whether the features that describe my larger genre-playlists were coherent enough to use as the classes of a classifier. I compared the performance of SVM’s with shallow multi-layer perceptrons.
|
||||
|
||||
@ -90,4 +84,4 @@ Similar to class rebalancing, the dataset also required processing. Before using
|
||||
|
||||
Instead of allowing this to be determined by a random split, the dataset was _stratified_ when splitting. This applies the given proportion of training to test set to each class during the split such that the same proportion of tracks occur in either dataset.
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/listening-analysis)
|
||||
[Source Code](https://github.com/Sarsoo/listening-analysis)
|
@ -2,12 +2,6 @@
|
||||
title: "Linear Predictive Speech Synthesiser"
|
||||
date: 2020-11-10T14:48:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Uni
|
||||
- Matlab
|
||||
- ML
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
During my speech & audio processing & recognition post-grad module, I completed two pieces of coursework. The first of which involved writing and analysing a speech synthesiser utilising linear predictive coding. The report achieved 95%.
|
||||
@ -16,7 +10,7 @@ During my speech & audio processing & recognition post-grad module, I completed
|
||||
|
||||
The report analysed two vowel segments in order to identify their fundamental frequencies and the first handful of formant frequencies. After this, linear predictive coefficients of varying orders were calculated and used in conjunction with the fundamental frequency to re-synthesise the vowel.
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/linear-predictive-speech-synth)
|
||||
[GitHub Repo](https://github.com/Sarsoo/linear-predictive-speech-synth)
|
||||
|
||||
[Read the report here.](final-report.pdf)
|
||||
|
||||
|
@ -2,12 +2,6 @@
|
||||
title: "Hidden Markov Model Training"
|
||||
date: 2021-01-05T11:12:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Uni
|
||||
- Python
|
||||
- ML
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
![state-topology](StateTopology.png)
|
||||
@ -30,6 +24,6 @@ From here, the coursework tested the ability to calculate and analyse various as
|
||||
|
||||
The above graph is presenting the __occupation likelihoods__ of each state at each time step or observation. It is the joint probability from the forward and backward likelihoods. From here it looks like the observations were taken from state 2 for 3 time-steps before swapping to state 1 for 4 time-steps and changing back to state 2 for the last one.
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/markov-models)
|
||||
[GitHub Repo](https://github.com/Sarsoo/markov-models)
|
||||
|
||||
[Read the report here.](report.pdf)
|
@ -2,8 +2,6 @@
|
||||
title: "Net-Zero Cable Repair Ship"
|
||||
date: 2021-01-10T18:44:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Uni
|
||||
---
|
||||
|
||||
I've just completed one of my post-grad modules which was structured as a multi-disciplinary group design project. A group of engineers of varying disciplines came together to design a subsea fibre-optic cable repair ship which is net-zero carbon.
|
||||
@ -32,6 +30,6 @@ Drones are used in subsea cable repair to locate, cut and retrieve each half of
|
||||
|
||||
This requires a number of advances including onboard power, a more advanced navigation system and a more flexible launch and recovery system.
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/MDDP-Cableship)
|
||||
[GitHub Repo](https://github.com/Sarsoo/MDDP-Cableship)
|
||||
|
||||
[Read the report here.](report-extra.pdf)
|
@ -2,11 +2,6 @@
|
||||
title: "More Sustainable Terraform: Breaking Down the Beast"
|
||||
date: 2023-09-10T08:34:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Terraform
|
||||
- Cloud
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
I [wrote earlier this year]({{< relref "infra" >}}) about jumping into Terraform to manage my infrastructure. I love the [idemptotent](https://en.wikipedia.org/wiki/Idempotence) behaviour and the way that the declarative format leads to a self-documenting, centralised repository of my resources across a variety of platforms.
|
||||
|
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 130 KiB |
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 88 KiB |
@ -4,12 +4,6 @@ date: 2021-01-19T14:23:40+00:00
|
||||
draft: false
|
||||
aliases:
|
||||
- /mixonomer
|
||||
tags:
|
||||
- Python
|
||||
- Javascript
|
||||
- Cloud
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
![ci badge](https://github.com/sarsoo/mixonomer/workflows/test%20and%20deploy/badge.svg)
|
||||
@ -54,8 +48,8 @@ The app sits in the background now, it has replaced [__Smarter Playlists__](http
|
||||
|
||||
# [Try It Out](https://mixonomer.sarsoo.xyz/)
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/Mixonomer)
|
||||
[Github](https://github.com/Sarsoo/Mixonomer)
|
||||
|
||||
[iOS ![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/Mixonomer-iOS)
|
||||
[iOS Github](https://github.com/Sarsoo/Mixonomer-iOS)
|
||||
|
||||
[C# ![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/Mixonomer.NET)
|
||||
[C# Github](https://github.com/Sarsoo/Mixonomer.NET)
|
@ -2,8 +2,6 @@
|
||||
title: "Nanotechnology Coursework"
|
||||
date: 2019-11-19T14:43:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Uni
|
||||
---
|
||||
|
||||
As part of my 3rd-year nanoscience and nanotechnology module, I wrote a report on a quantum well design and the nanomaterial-based cancer treatment, Abraxane.
|
||||
|
24
content/posts/overflow/index.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: "Overflow: Analysing UK Water Company Storm Overflow Usage"
|
||||
date: 2024-10-13T08:26:40+00:00
|
||||
tags:
|
||||
- C#
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
![ci](https://github.com/Sarsoo/Overflow/actions/workflows/ci.yml/badge.svg)
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/Overflow)
|
||||
|
||||
[![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)](https://git.sarsoo.xyz/sarsoo/-/packages/container/overflow)
|
||||
[![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://git.sarsoo.xyz/sarsoo/-/packages/helm/overflow)
|
||||
|
||||
# context
|
||||
|
||||
# blazor
|
||||
|
||||
# scraping
|
||||
|
||||
# mongo
|
||||
|
||||
# deployment
|
@ -2,9 +2,6 @@
|
||||
title: "ROS Labs"
|
||||
date: 2020-05-19T22:49:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Uni
|
||||
- C++
|
||||
---
|
||||
|
||||
One of my 3rd-year university modules was a robotics module working with the ROS ecosystem in Python and C++. The course worked through the theory of the field including navigation and mapping in the context of how this is implemented in ROS.
|
||||
|
@ -4,13 +4,6 @@ date: 2022-04-04T21:26:40+00:00
|
||||
draft: false
|
||||
aliases:
|
||||
- /selector
|
||||
tags:
|
||||
- Music
|
||||
- C#
|
||||
- Vue
|
||||
- Javascript
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
![ci](https://github.com/sarsoo/Selector/actions/workflows/ci.yml/badge.svg)
|
||||
|
@ -1,85 +0,0 @@
|
||||
---
|
||||
title: "Going all in on Tailscale"
|
||||
date: 2024-07-02T08:26:40+00:00
|
||||
tags:
|
||||
- DevOps
|
||||
- Networking
|
||||
categories:
|
||||
- Homelab
|
||||
---
|
||||
|
||||
I've been fiddling with Tailscale for a while. It has always seemed like a really cool piece of tech but there have been a few things that have held me back from going all in.
|
||||
|
||||
For context, Tailscale is a VPN platform built on top of Wireguard that builds a flat, programmable network. It has some helpful features to help keep clients connected in ways Wireguard won't, by default. This helps with clients that are behind complex NATs.
|
||||
|
||||
I've been running my own Wireguard tunnels for home access since before Tailscale came out and was always pretty impressed with what Wireguard represents over the alternatives. I ran an OpenVPN tunnel a long time ago and, to be honest, it was comparatively crap. Wireguard being just a key pair is so simple and the performance is basically native.
|
||||
|
||||
This meant that my remote access solution was heavily centred on my Pfsense router for both Wireguard tunnel hosting and all of the routing rules. These routing rules allowed me to build my reverse VPN solution and include any forward VPN connections.
|
||||
|
||||
_Here, I am using reverse and forward in the way that it is used when talking about Proxies, which isn't a way I have heard VPNs described before but should make describing it easier. Here, a **reverse** VPN is for accessing protected resources, in the way that an enterprise VPN functions. A **forward** VPN is for protecting outgoing traffic using a commercial service like Nord/Proton VPN._
|
||||
|
||||
# Sharing
|
||||
|
||||
The ability to share resources is a powerful addition to the overlay network that allows exporting and importing components from other people. Previously, if I have wanted to share resources and services with friends and family they would either need to be publically accessible or people would need to be onboarded to the self-hosted Wireguard which is a bit onerrous. With Tailscale, it's super simple just invite them to access a specific node with a link or an email.
|
||||
|
||||
You can then control what they can access further with ACL rules.
|
||||
|
||||
# Containers
|
||||
|
||||
While this is all very powerful, one of the things which finally got me to take another look was the ability to combine Tailscale and its Serve + Funnel features with individual Docker Containers. [Alex Kretzschmar did a video for Tailscale](https://tailscale.com/kb/1282/docker) where he lays out how this works. Essentially, instead of just adding a machine to Tailscale, you can add a single container.
|
||||
|
||||
This works by standing up a Tailscale container alongside the target and making the target use it as its network using `network_mode`. Under the hood, this makes the two containers share a network namespace and effectively share a `localhost`.
|
||||
|
||||
Combining this with the sharing capabilities, you no longer need to share a whole machine with someone else, instead you can share just one container.
|
||||
|
||||
## Serve + Funnel
|
||||
|
||||
I use a lot of NGINX, it's great. My Ansible setup templates my NGINX configs for me and it integrates with it really well. I'm also a big fan of Caddy, I tend to use this on my internet-facing services because the auto-provisioning and renewal of LetsEncrypt certificates really make life easy. What I'm saying is I like reverse proxies, they make me happy and they make life livable with a heavily containerised homelab.
|
||||
|
||||
Tailscale Serve and Tailscale Funnel are ways to reverse proxy natively in Tailscale and really supercharge Tailscale-connected containers. In short, Tailscale Serve reverse proxies ports to your tailnet while Funnel extends that to make that port publically accessible, *with a LetsEncrypt certificate by default*. This is super cool.
|
||||
|
||||
To be fair, directly connecting a container to the public internet without some sort of reverse proxy would probably be more pain than it's worth, few containers expose port 80 let alone 80 and 443 - *and this is a good thing, that's what reverse proxies are for*. but Serve + Funnel makes the whole thing work.
|
||||
|
||||
|
||||
# CI/CD
|
||||
|
||||
This is a bit of a rabbit hole, it's something I've done fairly recently and I'm not sure if I like it. It still sort of gives me weird vibes but it is undeniably powerful. You can create OAuth clients for Tailscale, allowing you to programmatically manage the network. You can use this to allow Github Actions runners to authenticate and gain access to your Tailnet.
|
||||
|
||||
Combine this with Tailscale SSH which allows you to replace the SSH keys auth layer with the ACLs of Tailscale and you have a way to let your CI/CD pipelines SSH into your Tailnet machines and deploy code that isn't cloud-hosted.
|
||||
|
||||
I really like how this closes the loop and helps implement full-throated CD. It also sort of scares me that Github can SSH into my infrastructure. Let's call it an experiment, we'll see if I stick with it. Don't get me wrong, it's ACL-ed to the eyeballs, but it still feels odd.
|
||||
|
||||
# Dopamine
|
||||
|
||||
A few things that I have managed to do with Tailscale that made me happy to wrap up.
|
||||
|
||||
## Itty bitty containers of DNS
|
||||
|
||||
You can set the DNS servers used by nodes on your Tailnet, you can also do split DNS where requests for specific domains are routed to specific servers. This was a bit of an itch for me, I use my own domain on my homelab and I wanted to be able to keep doing split DNS for my domain when on Tailscale.
|
||||
|
||||
To achieve this I deployed containers for Bind DNS server that are only available on my Tailnet. Ansible renders the Zone files so that when you are requesting from a Tailscale IP it returns the Tailscale IP for the machine instead of the `192.168.x.x` IP. The config looks a bit like this:
|
||||
|
||||
```
|
||||
acl "ts-subnet" { 100.64.0.0/10; };
|
||||
|
||||
view "tailscale" {
|
||||
match-clients { "ts-subnet"; };
|
||||
|
||||
<ZONE FILES>
|
||||
};
|
||||
|
||||
view "native" {
|
||||
match-clients { any; };
|
||||
|
||||
<ZONE FILES>
|
||||
};
|
||||
```
|
||||
|
||||
I also run three Bind containers, one at home, one in Linode and one in Oracle cloud. This not only adds redundancy but should also help with resolution speed when I'm not at home.
|
||||
|
||||
## Shhh, what SSH
|
||||
|
||||
There have been a few SSH scares recently, first the whole social engineering System D thing, [then the RCE](https://www.wiz.io/blog/cve-2024-6387-critical-rce-openssh). SSH is very secure and it's _proooobably_ fine to have it open on the internet, but with Tailscale you don't have to so I don't. That just makes me a bit happy.
|
||||
|
||||
|
||||
So, for now, I'm using Tailscale as my VPN solution. It's still got some drawbacks, but I'm having fun playing with it, and it has some real strengths over rolling your own Wireguard solution.
|
@ -2,12 +2,6 @@
|
||||
title: "Visual Search Report"
|
||||
date: 2019-12-03T14:26:40+00:00
|
||||
draft: false
|
||||
tags:
|
||||
- Uni
|
||||
- Matlab
|
||||
- ML
|
||||
categories:
|
||||
- Dev
|
||||
---
|
||||
|
||||
As part of my computer vision and pattern recognition module, I wrote and reported on a [visual search](https://en.wikipedia.org/wiki/Visual_search) system using the [MSRC v2 data set](http://download.microsoft.com/download/3/3/9/339D8A24-47D7-412F-A1E8-1A415BC48A15/msrc_objcategimagedatabase_v2.zip). Visual search is essentially what Google's reverse image search does. Given a target image, the system should be able to return similar images based on its content. The dataset is divided into 20 different categories including trees, cows and faces - a good visual search system will be able to retrieve the images of the same category as the subject before the other less relevant images.
|
||||
@ -76,4 +70,4 @@ From both of the above graphs, the L1 distance measure consistently outperformed
|
||||
|
||||
This work was one of my favourite pieces of coursework I did at uni. I really got sucked into playing with the different descriptors and visualisation. Once I was happy with the MATLAB syntax I kept adding to the system, finally writing an automation that allowed the surface plots seen above for the different grid sizes. These were huge calculations that basically did the whole visual search process from start to end over and over again for each grid dimension combination. It was the first time that I had to leave my computer working at my code; it was really rewarding when it was these that were specifically identified as making the piece worthy of full marks.
|
||||
|
||||
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/Sarsoo/visual-search)
|
||||
[GitHub Repo](https://github.com/Sarsoo/visual-search)
|
@ -18,6 +18,7 @@
|
||||
[<a href="{{ .Site.Params.commit }}/{{ .GitInfo.Hash }}">{{ .GitInfo.AbbreviatedHash }}</a>]
|
||||
{{ end }}
|
||||
|
||||
<img src="/images/andy.png" width="80px" class="footer-logo" />
|
||||
<img src="/images/andy.png" width="80px" />
|
||||
<span style="color: #b0b0b0">©</span>
|
||||
</section>
|
||||
</footer>
|
||||
|
21
layouts/partials/head.html
Normal file
@ -0,0 +1,21 @@
|
||||
{{ partial "head/meta-tags.html" . }}
|
||||
|
||||
{{ if .Permalink }}
|
||||
<link rel="canonical" href="{{ .Permalink }}">
|
||||
{{ end }}
|
||||
|
||||
{{ partialCached "head/theme-styles.html" . }}
|
||||
|
||||
{{ partialCached "head/color-scheme.html" . }}
|
||||
|
||||
{{ partialCached "head/custom-styles.html" . }}
|
||||
|
||||
{{ partialCached "head/custom-icons.html" . }}
|
||||
|
||||
{{ partial "head/alternative-output-formats.html" . }}
|
||||
|
||||
{{ partialCached "head/hugo-generator.html" . }}
|
||||
|
||||
{{ partial "head/extensions.html" . }}
|
||||
|
||||
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous"> -->
|
@ -1,4 +0,0 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Pirata+One&family=Silkscreen&display=swap">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Pirata+One&family=Silkscreen&display=swap" rel="stylesheet">
|
@ -1,3 +0,0 @@
|
||||
<div class="art-grid">
|
||||
{{ .Inner }}
|
||||
</div>
|
@ -1,3 +1 @@
|
||||
<div class="avatar">
|
||||
<img src="{{ .Get 0 }}" style="border-radius: 50%; width: 30rem; margin-left: auto; margin-right: auto; display: block;"/>
|
||||
</div>
|
||||
<img src="{{ .Get 0 }}" style="border-radius: 50%; width: 40rem; margin-left: auto; margin-right: auto; display: block;"/>
|
@ -1,12 +1,62 @@
|
||||
<div>
|
||||
<div class="box-link">
|
||||
<span aria-hidden="true"></span>
|
||||
<img loading="lazy" alt="" src="{{ .Get "src" }}">
|
||||
<div class="text-section">
|
||||
<p style="font-size:30px;">
|
||||
<a class="center-link-text" href="{{ .Get "href" }}">{{ .Get "title" }}</a>
|
||||
<div style="
|
||||
border: 50px black;
|
||||
position: relative;
|
||||
background-size: cover;
|
||||
background-position: 50%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;">
|
||||
<span aria-hidden="true" style="
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
opacity: .5;
|
||||
background-color: black;
|
||||
position: absolute;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
z-index: 1; "></span>
|
||||
<img loading="lazy" alt="" src="{{ .Get "src" }}" style="
|
||||
/* object-position:38% 50%; */
|
||||
z-index: 0;
|
||||
position: absolute;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%; height: 100%;
|
||||
object-fit: cover;
|
||||
outline: none;
|
||||
border: 4px solid black;
|
||||
border-radius: 10px;
|
||||
box-shadow: 5px 5px;">
|
||||
<div style="
|
||||
z-index: 1;
|
||||
margin-top: 50px;
|
||||
margin-bottom: 50px;
|
||||
margin-right: 5%;
|
||||
margin-left: 5%;">
|
||||
<p style="
|
||||
font-size:30px; color: white;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
text-shadow: 0px 2px black;">
|
||||
<a href="{{ .Get "href" }}"
|
||||
style="
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
text-underline-offset: 3px;
|
||||
text-decoration-skip-ink: all;">{{ .Get "title" }}</a>
|
||||
</p>{{if isset .Params "caption" }}
|
||||
<p class="silkscreen-regular">{{ .Get "caption" }}</p>{{end}}
|
||||
<p style="
|
||||
color: white;
|
||||
text-align: center;
|
||||
font-family: monospace;
|
||||
margin: auto;
|
||||
margin-top: 20px;
|
||||
text-shadow: 0px 1px black;">{{ .Get "caption" }}</p>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 329 B |
Before Width: | Height: | Size: 592 B |
@ -1,8 +0,0 @@
|
||||
{
|
||||
"short_name": "Sarsoo",
|
||||
"name": "Sarsoo",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#3f0aff",
|
||||
"background_color": "#ffffff"
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit f69d6d6da728790a5f0bb1c5e2047174bc8f185c
|
||||
Subproject commit e35f1da207cb25f4fce730177423d041cf90e7dd
|