- commit
- d77b910
- parent
- 85ad4b8
- author
- Eric Bower
- date
- 2024-01-09 19:43:00 +0000 UTC
Refactor docs (#68) Removed all docs associated with our services and moved them to a centralized source: https://pico.sh
+0,
-27
1@@ -1,27 +0,0 @@
2-name: pgs-static-site
3-on:
4- push:
5- branches:
6- - main
7-jobs:
8- static:
9- runs-on: ubuntu-latest
10- steps:
11- - uses: actions/checkout@v3
12- - name: Set outputs
13- id: vars
14- run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
15- - uses: actions/setup-go@v4
16- with:
17- go-version: '1.21'
18- - name: build pgs site
19- run: make pgs-static pgs-site
20- - name: publish to pgs
21- uses: picosh/pgs-action@v3
22- with:
23- user: hey
24- key: ${{ secrets.PRIVATE_KEY }}
25- src: './public/'
26- project: "pgs-${{ steps.vars.outputs.sha_short }}"
27- promote: "pgs-prod"
28- retain: "pgs-"
M
Makefile
+0,
-20
1@@ -76,26 +76,6 @@ build-%:
2 build: build-prose build-lists build-pastes build-imgs build-feeds build-pgs build-auth
3 .PHONY: build
4
5-pgs-static:
6- go build -o "build/pgs-static" "./cmd/pgs/static"
7-.PHONY: pgs-static
8-
9-pgs-site:
10- rm -rf public
11- mkdir public
12- PGS_EMAIL=hello@pico.sh PGS_DOMAIN=pgs.sh PGS_PROTOCOL=https ./build/pgs-static -out ./public
13- cp ./pgs/public/* ./public
14-.PHONY: pgs-site
15-
16-pgs-dev: pgs-static pgs-site
17- rsync -rv -e "ssh -p 2222" ./public/ erock@localhost:/pgs-local
18-.PHONY: pgs-dev
19-
20-pgs-deploy: pgs-static pgs-site
21- rsync -rv ./public/ hey@pgs.sh:/pgs-local
22- ssh hey@pgs.sh link pgs-prod pgs-local --write
23-.PHONY: pgs-site-deploy
24-
25 store-clean:
26 WRITE=$(WRITE) go run ./cmd/scripts/clean-object-store/clean.go
27 .PHONY: store-clean
+0,
-17
1@@ -1,17 +0,0 @@
2-package main
3-
4-import (
5- "flag"
6-
7- "github.com/picosh/pico/pgs"
8-)
9-
10-func main() {
11- out := flag.String("out", "./public", "output folder for static assets")
12- flag.Parse()
13- cfg := pgs.NewConfigSite()
14- err := pgs.GenStaticSite(*out, cfg)
15- if err != nil {
16- panic(err)
17- }
18-}
+0,
-4
1@@ -26,10 +26,6 @@ func createStaticRoutes() []shared.Route {
2 func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
3 routes := []shared.Route{
4 shared.NewRoute("GET", "/", shared.CreatePageHandler("html/marketing.page.tmpl")),
5- shared.NewRoute("GET", "/ops", shared.CreatePageHandler("html/ops.page.tmpl")),
6- shared.NewRoute("GET", "/privacy", shared.CreatePageHandler("html/privacy.page.tmpl")),
7- shared.NewRoute("GET", "/help", shared.CreatePageHandler("html/help.page.tmpl")),
8- shared.NewRoute("GET", "/check", shared.CheckHandler),
9 }
10
11 routes = append(
+0,
-152
1@@ -1,152 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}help -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="questions and answers" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Need help?</h1>
15- <p>Here are some common questions on using this platform that we would like to answer.</p>
16-</header>
17-<main>
18- <section id="permission-denied">
19- <h2 class="text-xl">
20- <a href="#permission-denied" rel="nofollow noopener">#</a>
21- I get a permission denied when trying to SSH
22- </h2>
23- <p>
24- Unfortunately SHA-2 RSA keys are <strong>not</strong> currently supported.
25- </p>
26- <p>
27- Unfortunately, due to a shortcoming in Go’s x/crypto/ssh package, Soft Serve does
28- not currently support access via new SSH RSA keys: only the old SHA-1 ones will work.
29- Until we sort this out you’ll either need an SHA-1 RSA key or a key with another
30- algorithm, e.g. Ed25519. Not sure what type of keys you have? You can check with the
31- following:
32- </p>
33- <pre>$ find ~/.ssh/id_*.pub -exec ssh-keygen -l -f {} \;</pre>
34- <p>If you’re curious about the inner workings of this problem have a look at:</p>
35- <ul>
36- <li><a href="https://github.com/golang/go/issues/37278">golang/go#37278</a></li>
37- <li><a href="https://go-review.googlesource.com/c/crypto/+/220037">go-review</a></li>
38- <li><a href="https://github.com/golang/crypto/pull/197">golang/crypto#197</a></li>
39- </ul>
40- </section>
41-
42- <section id="ssh-key">
43- <h2 class="text-xl">
44- <a href="#ssh-key" rel="nofollow noopener">#</a>
45- Generating a new SSH key
46- </h2>
47- <p>
48- <a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">Github reference</a>
49- </p>
50- <pre>ssh-keygen -t ed25519 -C "your_email@example.com"</pre>
51- <ol>
52- <li>When you're prompted to "Enter a file in which to save the key," press Enter. This accepts the default file location.</li>
53- <li>At the prompt, type a secure passphrase.</li>
54- </ol>
55- </section>
56-
57- <section id="digest-interval">
58- <h2 class="text-xl">
59- <a href="#digest-interval" rel="nofollow noopener">#</a>
60- Digest interval options
61- </h2>
62- <ul>
63- <li><code>10min</code></li>
64- <li><code>1hour</code></li>
65- <li><code>6hour</code></li>
66- <li><code>12hour</code></li>
67- <li><code>1day</code></li>
68- <li><code>7day</code></li>
69- <li><code>30day</code></li>
70- </ul>
71- </section>
72-
73- <section id="multiple-feeds">
74- <h2 class="text-xl">
75- <a href="#multiple-feeds" rel="nofollow noopener">#</a>
76- Can I create multiple email digests?
77- </h2>
78- <p>
79- You are free to upload as many email digests as you like,
80- referencing different rss feeds, emails, and digest intervals.
81- </p>
82- </section>
83-
84- <section id="inline-content">
85- <h2 class="text-xl">
86- <a href="#inline-content" rel="nofollow noopener">#</a>
87- Inline content
88- </h2>
89- <p>
90- By default we attempt to render all content within a feed as HTML inside an email digest.
91- Sometimes users just want us to send them the links so they can click on it and read the content
92- on the original website.
93- </p>
94- <p>If you don't want to see all the content, simply add a variable to your post:</p>
95- <pre>=: inline_content false</pre>
96- </section>
97-
98- <section id="post-update">
99- <h2 class="text-xl">
100- <a href="#post-update" rel="nofollow noopener">#</a>
101- How do I update a list?
102- </h2>
103- <p>
104- Updating a list requires that you update the source document and then run the <code>scp</code>
105- command again. If the filename remains the same, then the list will be updated.
106- </p>
107- </section>
108-
109- <section id="post-delete">
110- <h2 class="text-xl">
111- <a href="#post-delete" rel="nofollow noopener">#</a>
112- How do I delete a list?
113- </h2>
114- <p>
115- Because <code>scp</code> does not natively support deleting files, I didn't want to bake
116- that behavior into my ssh server.
117- </p>
118-
119- <p>
120- However, if a user wants to delete a post they can delete the contents of the file and
121- then upload it to our server. If the file contains 0 bytes, we will remove the post.
122- For example, if you want to delete <code>delete.txt</code> you could:
123- </p>
124-
125- <pre>
126-cp /dev/null delete.txt
127-scp ./delete.txt {{.Site.Domain}}:/</pre>
128-
129- <p>
130- Alternatively, you can go to <code>ssh {{.Site.Domain}}</code> and select "Manage posts."
131- Then you can highlight the post you want to delete and then press "X." It will ask for
132- confirmation before actually removing the list.
133- </p>
134- </section>
135-
136- <section id="multiple-accounts">
137- <h2 class="text-xl">
138- <a href="#multiple-accounts" rel="nofollow noopener">#</a>
139- Can I create multiple accounts?
140- </h2>
141- <p>
142- Yes! You can either a) create a new keypair and use that for authentication
143- or b) use the same keypair and ssh into our CMS using our special username
144- <code>ssh new@{{.Site.Domain}}</code>.
145- </p>
146- <p>
147- Please note that if you use the same keypair for multiple accounts, you will need to
148- always specify the user when logging into our CMS.
149- </p>
150- </section>
151-</main>
152-{{template "marketing-footer" .}}
153-{{end}}
1@@ -2,12 +2,5 @@
2 <footer>
3 <hr />
4 <p class="font-italic">Built and maintained by <a href="https://pico.sh">pico.sh</a>.</p>
5- <div>
6- <a href="/">home</a> |
7- <a href="https://lists.sh/spec">spec</a> |
8- <a href="/ops">ops</a> |
9- <a href="/help">help</a> |
10- <a href="https://github.com/picosh/pico">source</a>
11- </div>
12 </footer>
13 {{end}}
+4,
-78
1@@ -28,86 +28,12 @@
2
3 {{define "body"}}
4 <header class="text-center">
5- <h1 class="text-2xl font-bold">{{.Site.Domain}} [beta]</h1>
6+ <h1 class="text-2xl font-bold">{{.Site.Domain}}</h1>
7 <p class="text-lg">An rss email notification service</p>
8- <hr />
9+ <div>
10+ <a href="https://pico.sh/getting-started" class="btn-link mt inline-block">GET STARTED</a>
11+ </div>
12 </header>
13
14-<main>
15- <section>
16- <h2 class="text-lg font-bold">Features</h2>
17- <ul>
18- <li>Receive email digests for your RSS feeds</li>
19- <li>We try to render all content within the feed as HTML (<a href="/help#inline-content">with ability to disable it</a>)</li>
20- <li>Create 1-to-many email digests</li>
21- <li>Set digest interval from <a href="/help#digest-interval"><code>10min</code> to <code>30day</code></a></li>
22- </ul>
23- </section>
24-
25- <section>
26- <h2 class="text-lg font-bold">Create your account with Public-Key Cryptography</h2>
27- <p>To get started, simply ssh into our content management system:</p>
28- <pre>ssh new@{{.Site.Domain}}</pre>
29- <div class="text-sm font-italic note">
30- note: <code>new</code> is a special username that will always send you to account
31- creation.
32- </div>
33- <div class="text-sm font-italic note">
34- note: getting permission denied? <a href="/help#permission-denied">read this</a>
35- </div>
36- <p>
37- After that, set a username and email and then you're ready!
38- </p>
39- </section>
40-
41- <section>
42- <h2 class="text-lg font-bold">Subscribe to feeds</h2>
43- <p>
44- Use <a href="https://lists.sh/spec">lists.sh/spec</a> to create a txt file
45- named <code>daily.txt</code> (as an example).
46- </p>
47- <p>
48- Then add your email, the digest interval, and the rss feeds for which you
49- want to receive email notifications.
50- </p>
51- <pre>=: email rss@myemail.com
52-=: digest_interval 1day
53-=> https://hey.prose.sh/rss
54-=> https://hey.lists.sh/rss
55-=> https://erock.prose.sh/rss
56-</pre>
57- <p>
58- Then copy the file to our server
59- </p>
60- <pre>scp daily.txt {{.Site.Domain}}:/</pre>
61- <p>Using this example, we will send a daily digest of new posts to the email specified.</p>
62- </section>
63-
64- <section>
65- <h2 class="text-lg font-bold">Privacy</h2>
66- <p>
67- We don't do anything with your email besides send an email digest.
68- If you delete the post containing your email address, we no longer have you email address.
69- </p>
70- <p>
71- Posts are also not accessible by the public and we provide no endpoints to view these posts.
72- </p>
73- </section>
74-
75- <section>
76- <h2 class="text-lg font-bold">Plain text format</h2>
77- <p>A simple list specification that is flexible and with no frills.</p>
78- <p><a href="https://lists.sh/spec">specification</a></p>
79- </section>
80-
81- <section>
82- <h2 class="text-lg font-bold">Roadmap</h2>
83- <ol>
84- <li>Require user to click link in digest to continue receiving digest</li>
85- <li>Web RSS reader?</li>
86- </ol>
87- </section>
88-</main>
89-
90 {{template "marketing-footer" .}}
91 {{end}}
+0,
-126
1@@ -1,126 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}operations -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} operations" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Operations</h1>
15- <ul>
16- <li><a href="/privacy">privacy</a></li>
17- </ul>
18-</header>
19-<main>
20- <section>
21- <h2 class="text-xl">Purpose</h2>
22- <p>
23- {{.Site.Domain}} exists to allow people to receive daily email digests for their rss feeds.
24- </p>
25- </section>
26- <section>
27- <h2 class="text-xl">Ethics</h2>
28- <p>We are committed to:</p>
29- <ul>
30- <li>No browser-based tracking of visitor behavior.</li>
31- <li>No attempt to identify users.</li>
32- <li>Never sell any user or visitor data.</li>
33- <li>No ads — ever.</li>
34- </ul>
35- </section>
36- <section>
37- <h2 class="text-xl">Liability</h2>
38- <p>
39- The user expressly understands and agrees that Eric Bower, the operator of this website
40- shall not be liable, in law or in equity, to them or to any third party for any direct,
41- indirect, incidental, lost profits, special, consequential, punitive or exemplary damages.
42- </p>
43- </section>
44- <section>
45- <h2 class="text-xl">Analytics</h2>
46- <p>
47- We are committed to zero browser-based tracking or trying to identify visitors. This
48- means we do not try to understand the user based on cookies or IP address. We do not
49- store personally identifiable information.
50- </p>
51- <p>
52- However, in order to provide a better service, we do have some analytics on posts.
53- List of metrics we track for posts:
54- </p>
55- <ul>
56- <li>anonymous view counts</li>
57- </ul>
58- <p>
59- We might also inspect the headers of HTTP requests to determine some tertiary information
60- about the request. For example we might inspect the <code>User-Agent</code> or
61- <code>Referer</code> to filter out requests from bots.
62- </p>
63- </section>
64- <section>
65- <h2 class="text-xl">Account Terms</h2>
66- <p>
67- <ul>
68- <li>
69- The user is responsible for all content posted and all actions performed with
70- their account.
71- </li>
72- <li>
73- We reserve the right to disable or delete a user's account for any reason at
74- any time. We have this clause because, statistically speaking, there will be
75- people trying to do something nefarious.
76- </li>
77- </ul>
78- </p>
79- </section>
80- <section>
81- <h2 class="text-xl">Service Availability</h2>
82- <p>
83- We provide the {{.Site.Domain}} service on an "as is" and "as available" basis. We do not offer
84- service-level agreements but do take uptime seriously.
85- </p>
86- </section>
87- <section>
88- <h2 class="text-xl">Contact and Support</h2>
89- <p>
90- Email us at <a href="mailto:support@{{.Site.Domain}}">support@{{.Site.Domain}}</a>
91- with any questions.
92- </p>
93- </section>
94- <section>
95- <h2 class="text-xl">Acknowledgments</h2>
96- <p>
97- {{.Site.Domain}} is built with many open source technologies.
98- </p>
99- <p>
100- In particular we would like to thank:
101- </p>
102- <ul>
103- <li>
104- <span>The </span>
105- <a href="https://charm.sh">charm.sh</a>
106- <span> community</span>
107- </li>
108- <li>
109- <span>The </span>
110- <a href="https://go.dev">golang</a>
111- <span> community</span>
112- </li>
113- <li>
114- <span>The </span>
115- <a href="https://www.postgresql.org/">postgresql</a>
116- <span> community</span>
117- </li>
118- <li>
119- <span>The </span>
120- <a href="https://github.com/caddyserver/caddy">caddy</a>
121- <span> community</span>
122- </li>
123- </ul>
124- </section>
125-</main>
126-{{template "marketing-footer" .}}
127-{{end}}
+0,
-60
1@@ -1,60 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}privacy -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} privacy policy" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Privacy</h1>
15- <p>Details on our privacy and security approach.</p>
16-</header>
17-<main>
18- <section>
19- <h2 class="text-xl">Account Data</h2>
20- <p>
21- In order to have a functional account at {{.Site.Domain}}, we need to store
22- your public key. That is the only piece of information we record for a user.
23- </p>
24- <p>
25- In order to email the user their RSS digest, we need to capture an email address.
26- We do not use this email address for anything other than sending digest emails.
27- We do not scrape posts and keep any email addresses.
28- If you delete a post containing an email address then that email address is no longer in our system.
29- </p>
30- <p>
31- Because we use public-key cryptography, our security posture is a battle-tested
32- and proven technique for authentication.
33- </p>
34- </section>
35-
36- <section>
37- <h2 class="text-xl">Third parties</h2>
38- <p>
39- We have a strong commitment to never share any user data with any third-parties.
40- </p>
41- </section>
42-
43- <section>
44- <h2 class="text-xl">Service Providers</h2>
45- <ul>
46- <li>
47- <span>We host our server on </span>
48- <a href="https://www.oracle.com/cloud/">oracle cloud</a>
49- </li>
50- </ul>
51- </section>
52-
53- <section>
54- <h2 class="text-xl">Cookies</h2>
55- <p>
56- We do not use any cookies, not even for account authentication.
57- </p>
58- </section>
59-</main>
60-{{template "marketing-footer" .}}
61-{{end}}
+23,
-0
1@@ -17,6 +17,7 @@
2
3 @media (prefers-color-scheme: light) {
4 :root {
5+ --main-hue: 250;
6 --white: #6a737d;
7 --code: #fff8d3;
8 --code-border: #f0d547;
9@@ -34,6 +35,7 @@
10
11 @media (prefers-color-scheme: dark) {
12 :root {
13+ --main-hue: 250;
14 --white: #f2f2f2;
15 --code: #414558;
16 --code-border: #252525;
17@@ -271,6 +273,23 @@ figure {
18 margin: 0;
19 }
20
21+.btn-link {
22+ border: 3px solid hsl(var(--main-hue), 92%, 66%);
23+ background-color: hsl(var(--main-hue), 92%, 66%);
24+ padding: 0.5rem 1rem;
25+ border-radius: 0.25rem;
26+ box-shadow: 0px 1px 2px 0px black;
27+ color: var(--white);
28+ text-decoration: none;
29+ font-weight: bold;
30+}
31+
32+.btn-link:visited,
33+.btn-link:visited:hover,
34+.btn-link:hover {
35+ color: var(--white);
36+}
37+
38 .post-date {
39 width: 130px;
40 }
41@@ -319,6 +338,10 @@ figure {
42 display: inline;
43 }
44
45+.inline-block {
46+ display: inline-block;
47+}
48+
49 .flex {
50 display: flex;
51 }
+0,
-38
1@@ -421,40 +421,6 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
2 }
3 }
4
5-func transparencyHandler(w http.ResponseWriter, r *http.Request) {
6- dbpool := shared.GetDB(r)
7- logger := shared.GetLogger(r)
8- cfg := shared.GetCfg(r)
9-
10- analytics, err := dbpool.FindSiteAnalytics(cfg.Space)
11- if err != nil {
12- logger.Error(err)
13- http.Error(w, err.Error(), http.StatusInternalServerError)
14- return
15- }
16-
17- ts, err := template.ParseFiles(
18- cfg.StaticPath("html/transparency.page.tmpl"),
19- cfg.StaticPath("html/footer.partial.tmpl"),
20- cfg.StaticPath("html/marketing-footer.partial.tmpl"),
21- cfg.StaticPath("html/base.layout.tmpl"),
22- )
23-
24- if err != nil {
25- http.Error(w, err.Error(), http.StatusInternalServerError)
26- }
27-
28- data := TransparencyPageData{
29- Site: *cfg.GetSiteData(),
30- Analytics: analytics,
31- }
32- err = ts.Execute(w, data)
33- if err != nil {
34- logger.Error(err)
35- http.Error(w, err.Error(), http.StatusInternalServerError)
36- }
37-}
38-
39 func rssBlogHandler(w http.ResponseWriter, r *http.Request) {
40 username := shared.GetUsernameFromRequest(r)
41 dbpool := shared.GetDB(r)
42@@ -647,10 +613,6 @@ func createStaticRoutes() []shared.Route {
43 func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
44 routes := []shared.Route{
45 shared.NewRoute("GET", "/", shared.CreatePageHandler("html/marketing.page.tmpl")),
46- shared.NewRoute("GET", "/ops", shared.CreatePageHandler("html/ops.page.tmpl")),
47- shared.NewRoute("GET", "/privacy", shared.CreatePageHandler("html/privacy.page.tmpl")),
48- shared.NewRoute("GET", "/help", shared.CreatePageHandler("html/help.page.tmpl")),
49- shared.NewRoute("GET", "/transparency", transparencyHandler),
50 shared.NewRoute("GET", "/check", shared.CheckHandler),
51 }
52
+0,
-172
1@@ -1,172 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}help -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="questions and answers" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Need help?</h1>
15- <p>Here are some common questions on using this platform that we would like to answer.</p>
16-</header>
17-<main>
18- <section id="permission-denied">
19- <h2 class="text-xl">
20- <a href="#permission-denied" rel="nofollow noopener">#</a>
21- I get a permission denied when trying to SSH
22- </h2>
23- <p>
24- Unfortunately SHA-2 RSA keys are <strong>not</strong> currently supported.
25- </p>
26- <p>
27- Unfortunately, due to a shortcoming in Go’s x/crypto/ssh package, we
28- not currently support access via new SSH RSA keys: only the old SHA-1 ones will work.
29- Until we sort this out you’ll either need an SHA-1 RSA key or a key with another
30- algorithm, e.g. Ed25519. Not sure what type of keys you have? You can check with the
31- following:
32- </p>
33- <pre>$ find ~/.ssh/id_*.pub -exec ssh-keygen -l -f {} \;</pre>
34- <p>If you’re curious about the inner workings of this problem have a look at:</p>
35- <ul>
36- <li><a href="https://github.com/golang/go/issues/37278">golang/go#37278</a></li>
37- <li><a href="https://go-review.googlesource.com/c/crypto/+/220037">go-review</a></li>
38- <li><a href="https://github.com/golang/crypto/pull/197">golang/crypto#197</a></li>
39- </ul>
40- </section>
41-
42- <section id="blog-ssh-key">
43- <h2 class="text-xl">
44- <a href="#blog-ssh-key" rel="nofollow noopener">#</a>
45- Generating a new SSH key
46- </h2>
47- <p>
48- <a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">Github reference</a>
49- </p>
50- <pre>ssh-keygen -t ed25519 -C "your_email@example.com"</pre>
51- <ol>
52- <li>When you're prompted to "Enter a file in which to save the key," press Enter. This accepts the default file location.</li>
53- <li>At the prompt, type a secure passphrase.</li>
54- </ol>
55- </section>
56-
57- <section id="web-optimized">
58- <h2 class="text-xl">
59- <a href="#web-optimized" rel="nofollow noopener">#</a>
60- Web optimized
61- </h2>
62- <p>
63- When a user uploads an image, we immediately convert it to <code>webp</code>.
64- Then we have an API that serves those web optimized images.
65- </p>
66- </section>
67-
68- <section id="integrations">
69- <h2 class="text-xl">
70- <a href="#integrations" rel="nofollow noopener">#</a>
71- How does imgs integrate with other pico services?
72- </h2>
73- <p>
74- We allow any of our other services to upload images from those services to imgs.
75- For example, if you want to upload images for prose.sh all you have to do is include
76- your images in the <code>rsync</code> or <code>scp</code> command.
77- </p>
78- <pre>scp profile.jpg user@prose.sh:/</pre>
79- <p>Then when you want to reference the file, you can reference it like so:</p>
80- <pre>![profile pic](/profile.jpg)</pre>
81- </section>
82-
83- <section id="file-types">
84- <h2 class="text-xl">
85- <a href="#file-types" rel="nofollow noopener">#</a>
86- What file types are supported?
87- </h2>
88- <ul>
89- <li>jpg</li>
90- <li>png</li>
91- <li>gif</li>
92- <li>webp</li>
93- <li>svg</li>
94- </ul>
95- </section>
96-
97- <section id="image-manipulation">
98- <h2 class="text-xl">
99- <a href="#image-manipulation" rel="nofollow noopener">#</a>
100- Image manipulation
101- </h2>
102- <p>
103- We have an API that allows users to resize images on-the-fly. Currently
104- we only support downscaling.
105- </p>
106-
107- <pre>[!profile](/profile/x500) # auto scale width
108-[!profile](/profile/500x500) # scale width and height
109-[!profile](/profile/500x) # auto scale height</pre>
110- </section>
111-
112- <section id="img-update">
113- <h2 class="text-xl">
114- <a href="#img-update" rel="nofollow noopener">#</a>
115- How do I update a img?
116- </h2>
117- <p>
118- Updating a img requires that you update the source document and then run the <code>scp</code>
119- command again. If the filename remains the same, then the img will be updated.
120- </p>
121- </section>
122-
123- <section id="img-delete">
124- <h2 class="text-xl">
125- <a href="#img-delete" rel="nofollow noopener">#</a>
126- How do I delete a img?
127- </h2>
128- <p>
129- Because <code>scp</code> does not natively support deleting files, I didn't want to bake
130- that behavior into my ssh server.
131- </p>
132-
133- <p>
134- However, if a user wants to delete a img they can delete the contents of the file and
135- then upload it to our server. If the file contains 0 bytes, we will remove the img.
136- For example, if you want to delete <code>delete.jpg</code> you could:
137- </p>
138-
139- <pre>
140-cp /dev/null delete.jpg
141-scp ./delete.jpg {{.Site.Domain}}:/</pre>
142-
143- <p>
144- Alternatively, you can go to <code>ssh {{.Site.Domain}}</code> and select "Manage img."
145- Then you can highlight the img you want to delete and then press "X." It will ask for
146- confirmation before actually removing the img.
147- </p>
148- </section>
149-
150- <section id="custom-domain">
151- <h2 class="text-xl">
152- <a href="#custom-domain" rel="nofollow noopener">#</a>
153- Setup a custom domain
154- </h2>
155- <p>
156- A blog can be accessed from a custom domain.
157- HTTPS will be automatically enabled and a certificate will be retrieved
158- from <a href="https://letsencrypt.org/">Let's Encrypt</a>. In order for this to work,
159- 2 DNS records need to be created:
160- </p>
161-
162- <p>CNAME for the domain to imgs (subdomains or DNS hosting with CNAME flattening) or A record</p>
163- <pre>CNAME subdomain.yourcustomdomain.com -> imgs.sh</pre>
164- <p>Resulting in:</p>
165- <pre>subdomain.yourcustomdomain.com. 300 IN CNAME imgs.sh.</pre>
166- <p>And a TXT record to tell Prose what blog is hosted on that domain at the subdomain entry _imgs</p>
167- <pre>TXT _imgs.subdomain.yourcustomdomain.com -> yourusername</pre>
168- <p>Resulting in:</p>
169- <pre>_imgs.subdomain.yourcustomdomain.com. 300 IN TXT "hey"</pre>
170- </section>
171-</main>
172-{{template "marketing-footer" .}}
173-{{end}}
1@@ -3,11 +3,7 @@
2 <hr />
3 <p class="font-italic">Built and maintained by <a href="https://pico.sh">pico.sh</a>.</p>
4 <div>
5- <a href="/">home</a> |
6- <a href="/ops">ops</a> |
7- <a href="/help">help</a> |
8- <a href="/rss">rss</a> |
9- <a href="https://github.com/picosh/pico">source</a>
10+ <a href="/rss">rss</a>
11 </div>
12 </footer>
13 {{end}}
+3,
-73
1@@ -30,80 +30,10 @@
2 <header class="text-center">
3 <h1 class="text-2xl font-bold">{{.Site.Domain}}</h1>
4 <p class="text-lg">image hosting for hackers</p>
5- <hr />
6+ <div>
7+ <a href="https://pico.sh/getting-started" class="btn-link mt inline-block">GET STARTED</a>
8+ </div>
9 </header>
10
11-<main>
12- <section>
13- <h2 class="text-lg font-bold">Examples</h2>
14- <p><a href="https://erock.imgs.sh">feed</a></p>
15- </section>
16-
17- <section>
18- <h2 class="text-lg font-bold">Features</h2>
19- <ul>
20- <li>Delightful terminal workflow</li>
21- <li>Share public images from the terminal</li>
22- <li>Seamless integration with other pico services (e.g. <a href="https://prose.sh">prose</a>)</li>
23- <li>Images are web optimized by default</li>
24- <li>API to modify images on-the-fly (e.g. dimensions)</li>
25- <li>Hotlinking encouraged!</li>
26- <li>No javascript</li>
27- <li>No ads</li>
28- <li>10MB max image size</li>
29- <li>1GB max storage</li>
30- </ul>
31- </section>
32-
33- <section>
34- <h2 class="text-lg font-bold">Create your account with Public-Key Cryptography</h2>
35- <p>We don't want your email address.</p>
36- <p>To get started, simply ssh into our content management system:</p>
37- <pre>ssh new@{{.Site.Domain}}</pre>
38- <div class="text-sm font-italic note">
39- note: <code>new</code> is a special username that will always send you to account
40- creation, even with multiple accounts associated with your key-pair.
41- </div>
42- <div class="text-sm font-italic note">
43- note: getting permission denied? <a href="/help#permission-denied">read this</a>
44- </div>
45- <p>
46- After that, just set a username and you're ready to start writing! When you SSH
47- again, use your username that you set in the CMS.
48- </p>
49- </section>
50-
51- <section>
52- <h2 class="text-lg font-bold">Publish your images with one command</h2>
53- <p>
54- When your image is ready to be published, copy the file to our server with a familiar
55- command:
56- </p>
57- <pre>rsync my-image.jpg {{.Site.Domain}}:/</pre>
58- <p>We'll either create or update the images for you.</p>
59- </section>
60-
61- <section>
62- <h2 class="text-lg font-bold">Philosophy</h2>
63- <p>
64- Sharing photos is such an important part of the web. From writing a blog to creating
65- memes, we want to enable hackers to quickly share images while never leaving the terminal.
66- </p>
67- <p>
68- We built imgs out of necessity, it's something we really wanted as part of the pico
69- ecosystem and are excited to share it with you.
70- </p>
71- <p>Read more about team pico's philosophy <a href="https://pico.sh">here</a>.</p>
72- </section>
73-
74- <section>
75- <h2 class="text-lg font-bold">Roadmap</h2>
76- <ol>
77- <li>Ability to create albums</li>
78- <li>Device optimized images</li>
79- </ol>
80- </section>
81-</main>
82-
83 {{template "marketing-footer" .}}
84 {{end}}
+0,
-147
1@@ -1,147 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}operations -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} operations" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Operations</h1>
15- <ul>
16- <li><a href="/privacy">privacy</a></li>
17- <li><a href="/transparency">transparency</a></li>
18- </ul>
19-</header>
20-<main>
21- <section>
22- <h2 class="text-xl">Purpose</h2>
23- <p>
24- {{.Site.Domain}} exists to allow people to create and share their thoughts
25- without the need to set up their own server or be part of a platform
26- that shows ads or tracks its users.
27- </p>
28- </section>
29- <section>
30- <h2 class="text-xl">Ethics</h2>
31- <p>We are committed to:</p>
32- <ul>
33- <li>No browser-based tracking of visitor behavior.</li>
34- <li>No attempt to identify users.</li>
35- <li>Never sell any user or visitor data.</li>
36- <li>No ads — ever.</li>
37- </ul>
38- </section>
39- <section>
40- <h2 class="text-xl">Code of Content Publication</h2>
41- <p>
42- Content in {{.Site.Domain}} blogs is unfiltered and unmonitored. Users are free to publish any
43- combination of words and pixels except for: content of animosity or disparagement of an
44- individual or a group on account of a group characteristic such as race, color, national
45- origin, sex, disability, religion, or sexual orientation, which will be taken down
46- immediately.
47- </p>
48- <p>
49- If one notices something along those lines in a blog please let us know at
50- <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>.
51- </p>
52- </section>
53- <section>
54- <h2 class="text-xl">Liability</h2>
55- <p>
56- The user expressly understands and agrees that Eric Bower and Antonio Mika, the operator of this website
57- shall not be liable, in law or in equity, to them or to any third party for any direct,
58- indirect, incidental, lost profits, special, consequential, punitive or exemplary damages.
59- </p>
60- </section>
61- <section>
62- <h2 class="text-xl">Analytics</h2>
63- <p>
64- We are committed to zero browser-based tracking or trying to identify visitors. This
65- means we do not try to understand the user based on cookies or IP address. We do not
66- store personally identifiable information.
67- </p>
68- <p>
69- However, in order to provide a better service, we do have some analytics on posts.
70- List of metrics we track for posts:
71- </p>
72- <ul>
73- <li>anonymous view counts</li>
74- </ul>
75- <p>
76- We might also inspect the headers of HTTP requests to determine some tertiary information
77- about the request. For example we might inspect the <code>User-Agent</code> or
78- <code>Referer</code> to filter out requests from bots.
79- </p>
80- </section>
81- <section>
82- <h2 class="text-xl">Account Terms</h2>
83- <p>
84- <ul>
85- <li>
86- The user is responsible for all content posted and all actions performed with
87- their account.
88- </li>
89- <li>
90- We reserve the right to disable or delete a user's account for any reason at
91- any time. We have this clause because, statistically speaking, there will be
92- people trying to do something nefarious.
93- </li>
94- </ul>
95- </p>
96- </section>
97- <section>
98- <h2 class="text-xl">Service Availability</h2>
99- <p>
100- We provide the {{.Site.Domain}} service on an "as is" and "as available" basis. We do not offer
101- service-level agreements but do take uptime seriously.
102- </p>
103- </section>
104- <section>
105- <h2 class="text-xl">Contact and Support</h2>
106- <p>
107- Email us at <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>
108- with any questions.
109- </p>
110- </section>
111- <section>
112- <h2 class="text-xl">Acknowledgments</h2>
113- <p>
114- {{.Site.Domain}} was inspired by <a href="https://mataroa.blog">Mataroa Blog</a>
115- and <a href="https://bearblog.dev/">Bear Blog</a>.
116- </p>
117- <p>
118- {{.Site.Domain}} is built with many open source technologies.
119- </p>
120- <p>
121- In particular we would like to thank:
122- </p>
123- <ul>
124- <li>
125- <span>The </span>
126- <a href="https://charm.sh">charm.sh</a>
127- <span> community</span>
128- </li>
129- <li>
130- <span>The </span>
131- <a href="https://go.dev">golang</a>
132- <span> community</span>
133- </li>
134- <li>
135- <span>The </span>
136- <a href="https://www.postgresql.org/">postgresql</a>
137- <span> community</span>
138- </li>
139- <li>
140- <span>The </span>
141- <a href="https://github.com/caddyserver/caddy">caddy</a>
142- <span> community</span>
143- </li>
144- </ul>
145- </section>
146-</main>
147-{{template "marketing-footer" .}}
148-{{end}}
+0,
-54
1@@ -1,54 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}privacy -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} privacy policy" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Privacy</h1>
15- <p>Details on our privacy and security approach.</p>
16-</header>
17-<main>
18- <section>
19- <h2 class="text-xl">Account Data</h2>
20- <p>
21- In order to have a functional account at {{.Site.Domain}}, we need to store
22- your public key. That is the only piece of information we record for a user.
23- </p>
24- <p>
25- Because we use public-key cryptography, our security posture is a battle-tested
26- and proven technique for authentication.
27- </p>
28- </section>
29-
30- <section>
31- <h2 class="text-xl">Third parties</h2>
32- <p>
33- We have a strong commitment to never share any user data with any third-parties.
34- </p>
35- </section>
36-
37- <section>
38- <h2 class="text-xl">Service Providers</h2>
39- <ul>
40- <li>
41- <span>We host our server on </span>
42- <a href="https://www.oracle.com/cloud/">oracle cloud</a>
43- </li>
44- </ul>
45- </section>
46-
47- <section>
48- <h2 class="text-xl">Cookies</h2>
49- <p>
50- We do not use any cookies, not even account authentication.
51- </p>
52- </section>
53-</main>
54-{{template "marketing-footer" .}}
55-{{end}}
+0,
-59
1@@ -1,59 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}transparency -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="full transparency of analytics and cost at {{.Site.Domain}}" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Transparency</h1>
15- <hr />
16-</header>
17-<main>
18- <section>
19- <h2 class="text-xl">Analytics</h2>
20- <p>
21- Here are some interesting stats on usage.
22- </p>
23-
24- <article>
25- <h2 class="text-lg">Total users</h2>
26- <div>{{.Analytics.TotalUsers}}</div>
27- </article>
28-
29- <article>
30- <h2 class="text-lg">New users in the last month</h2>
31- <div>{{.Analytics.UsersLastMonth}}</div>
32- </article>
33-
34- <article>
35- <h2 class="text-lg">Total images</h2>
36- <div>{{.Analytics.TotalPosts}}</div>
37- </article>
38-
39- <article>
40- <h2 class="text-lg">New images in the last month</h2>
41- <div>{{.Analytics.PostsLastMonth}}</div>
42- </article>
43-
44- <article>
45- <h2 class="text-lg">Users with at least one image</h2>
46- <div>{{.Analytics.UsersWithPost}}</div>
47- </article>
48- </section>
49-
50- <section>
51- <h2 class="text-xl">Service maintenance costs</h2>
52- <ul>
53- <li>Domain name $3.25/mo</li>
54- <li>Server $0.00/mo</li>
55- <li>Programmer $0.00/mo</li>
56- </ul>
57- </section>
58-</main>
59-{{template "marketing-footer" .}}
60-{{end}}
+23,
-0
1@@ -17,6 +17,7 @@
2
3 @media (prefers-color-scheme: light) {
4 :root {
5+ --main-hue: 250;
6 --white: #6a737d;
7 --code: #fff8d3;
8 --code-border: #f0d547;
9@@ -34,6 +35,7 @@
10
11 @media (prefers-color-scheme: dark) {
12 :root {
13+ --main-hue: 250;
14 --white: #f2f2f2;
15 --code: #414558;
16 --code-border: #252525;
17@@ -271,6 +273,23 @@ figure {
18 margin: 0;
19 }
20
21+.btn-link {
22+ border: 3px solid hsl(var(--main-hue), 92%, 66%);
23+ background-color: hsl(var(--main-hue), 92%, 66%);
24+ padding: 0.5rem 1rem;
25+ border-radius: 0.25rem;
26+ box-shadow: 0px 1px 2px 0px black;
27+ color: var(--white);
28+ text-decoration: none;
29+ font-weight: bold;
30+}
31+
32+.btn-link:visited,
33+.btn-link:visited:hover,
34+.btn-link:hover {
35+ color: var(--white);
36+}
37+
38 .post-date {
39 width: 130px;
40 }
41@@ -319,6 +338,10 @@ figure {
42 display: inline;
43 }
44
45+.inline-block {
46+ display: inline-block;
47+}
48+
49 .flex {
50 display: flex;
51 }
+1,
-40
1@@ -383,40 +383,6 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
2 }
3 }
4
5-func transparencyHandler(w http.ResponseWriter, r *http.Request) {
6- dbpool := shared.GetDB(r)
7- logger := shared.GetLogger(r)
8- cfg := shared.GetCfg(r)
9-
10- analytics, err := dbpool.FindSiteAnalytics(cfg.Space)
11- if err != nil {
12- logger.Error(err)
13- http.Error(w, err.Error(), http.StatusInternalServerError)
14- return
15- }
16-
17- ts, err := template.ParseFiles(
18- cfg.StaticPath("html/transparency.page.tmpl"),
19- cfg.StaticPath("html/footer.partial.tmpl"),
20- cfg.StaticPath("html/marketing-footer.partial.tmpl"),
21- cfg.StaticPath("html/base.layout.tmpl"),
22- )
23-
24- if err != nil {
25- http.Error(w, err.Error(), http.StatusInternalServerError)
26- }
27-
28- data := TransparencyPageData{
29- Site: *cfg.GetSiteData(),
30- Analytics: analytics,
31- }
32- err = ts.Execute(w, data)
33- if err != nil {
34- logger.Error(err)
35- http.Error(w, err.Error(), http.StatusInternalServerError)
36- }
37-}
38-
39 func readHandler(w http.ResponseWriter, r *http.Request) {
40 dbpool := shared.GetDB(r)
41 logger := shared.GetLogger(r)
42@@ -689,12 +655,7 @@ func createStaticRoutes() []shared.Route {
43
44 func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
45 routes := []shared.Route{
46- shared.NewRoute("GET", "/", shared.CreatePageHandler("html/marketing.page.tmpl")),
47- shared.NewRoute("GET", "/spec", shared.CreatePageHandler("html/spec.page.tmpl")),
48- shared.NewRoute("GET", "/ops", shared.CreatePageHandler("html/ops.page.tmpl")),
49- shared.NewRoute("GET", "/privacy", shared.CreatePageHandler("html/privacy.page.tmpl")),
50- shared.NewRoute("GET", "/help", shared.CreatePageHandler("html/help.page.tmpl")),
51- shared.NewRoute("GET", "/transparency", transparencyHandler),
52+ shared.NewRoute("GET", "/", readHandler),
53 shared.NewRoute("GET", "/read", readHandler),
54 shared.NewRoute("GET", "/check", shared.CheckHandler),
55 }
+0,
-232
1@@ -1,232 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}help -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="questions and answers" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Need help?</h1>
15- <p>Here are some common questions on using this platform that we would like to answer.</p>
16-</header>
17-<main>
18- <section id="permission-denied">
19- <h2 class="text-xl">
20- <a href="#permission-denied" rel="nofollow noopener">#</a>
21- I get a permission denied when trying to SSH
22- </h2>
23- <p>
24- Unfortunately SHA-2 RSA keys are <strong>not</strong> currently supported.
25- </p>
26- <p>
27- Unfortunately, due to a shortcoming in Go’s x/crypto/ssh package, Soft Serve does
28- not currently support access via new SSH RSA keys: only the old SHA-1 ones will work.
29- Until we sort this out you’ll either need an SHA-1 RSA key or a key with another
30- algorithm, e.g. Ed25519. Not sure what type of keys you have? You can check with the
31- following:
32- </p>
33- <pre>$ find ~/.ssh/id_*.pub -exec ssh-keygen -l -f {} \;</pre>
34- <p>If you’re curious about the inner workings of this problem have a look at:</p>
35- <ul>
36- <li><a href="https://github.com/golang/go/issues/37278">golang/go#37278</a></li>
37- <li><a href="https://go-review.googlesource.com/c/crypto/+/220037">go-review</a></li>
38- <li><a href="https://github.com/golang/crypto/pull/197">golang/crypto#197</a></li>
39- </ul>
40- </section>
41-
42- <section id="ssh-key">
43- <h2 class="text-xl">
44- <a href="#ssh-key" rel="nofollow noopener">#</a>
45- Generating a new SSH key
46- </h2>
47- <p>
48- <a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">Github reference</a>
49- </p>
50- <pre>ssh-keygen -t ed25519 -C "your_email@example.com"</pre>
51- <ol>
52- <li>When you're prompted to "Enter a file in which to save the key," press Enter. This accepts the default file location.</li>
53- <li>At the prompt, type a secure passphrase.</li>
54- </ol>
55- </section>
56-
57- <section id="blog-structure">
58- <h2 class="text-xl">
59- <a href="#blog-structure" rel="nofollow noopener">#</a>
60- What should my blog folder look like?
61- </h2>
62- <p>
63- Currently {{.Site.Domain}} only supports a flat folder structure. Therefore,
64- <code>scp -r</code> is not permitted. We also only allow <code>.txt</code> files to be
65- uploaded.
66- </p>
67- <p>
68- <a href="https://github.com/neurosnap/lists-blog">Here is the source to my blog on this platform</a>
69- </p>
70- <p>
71- Below is an example of what your blog folder should look like:
72- </p>
73- <pre>blog/
74-first-post.txt
75-second-post.txt
76-third-post.txt</pre>
77- </p>
78- <p>
79- Underscores and hyphens are permitted and will be automatically removed from the title of the list.
80- </p>
81- </section>
82-
83- <section id="post-update">
84- <h2 class="text-xl">
85- <a href="#post-update" rel="nofollow noopener">#</a>
86- How do I update a list?
87- </h2>
88- <p>
89- Updating a list requires that you update the source document and then run the <code>scp</code>
90- command again. If the filename remains the same, then the list will be updated.
91- </p>
92- </section>
93-
94- <section id="post-delete">
95- <h2 class="text-xl">
96- <a href="#post-delete" rel="nofollow noopener">#</a>
97- How do I delete a list?
98- </h2>
99- <p>
100- Because <code>scp</code> does not natively support deleting files, I didn't want to bake
101- that behavior into my ssh server.
102- </p>
103-
104- <p>
105- However, if a user wants to delete a post they can delete the contents of the file and
106- then upload it to our server. If the file contains 0 bytes, we will remove the post.
107- For example, if you want to delete <code>delete.txt</code> you could:
108- </p>
109-
110- <pre>
111-cp /dev/null delete.txt
112-scp ./delete.txt {{.Site.Domain}}:/</pre>
113-
114- <p>
115- Alternatively, you can go to <code>ssh {{.Site.Domain}}</code> and select "Manage posts."
116- Then you can highlight the post you want to delete and then press "X." It will ask for
117- confirmation before actually removing the list.
118- </p>
119- </section>
120-
121- <section id="blog-upload-single-file">
122- <h2 class="text-xl">
123- <a href="#blog-upload-single-file" rel="nofollow noopener">#</a>
124- When I want to publish a new post, do I have to upload all posts everytime?
125- </h2>
126- <p>
127- Nope! Just <code>scp</code> the file you want to publish. For example, if you created
128- a new post called <code>taco-tuesday.txt</code> then you would publish it like this:
129- </p>
130- <pre>scp ./taco-tuesday.txt {{.Site.Domain}}:</pre>
131- </section>
132-
133- <section id="blog-header">
134- <h2 class="text-xl">
135- <a href="#blog-header" rel="nofollow noopener">#</a>
136- How do I change my blog's name?
137- </h2>
138- <p>
139- All you have to do is create a post titled <code>_header.txt</code> and add some
140- information to the list.
141- </p>
142- <pre>=: title My new blog!
143-=: description My blog description!
144-=> https://xyz.com website
145-=> https://twitter.com/xyz twitter</pre>
146- <ul>
147- <li><code>title</code> will change your blog name</li>
148- <li><code>description</code> will add a blurb right under your blog name (and add meta descriptions)</li>
149- <li>The links will show up next to the <code>rss</code> link to your blog
150- </ul>
151- </section>
152-
153- <section id="blog-readme">
154- <h2 class="text-xl">
155- <a href="#blog-readme" rel="nofollow noopener">#</a>
156- How do I add an introduction to my blog?
157- </h2>
158- <p>
159- All you have to do is create a post titled <code>_readme.txt</code> and add some
160- information to the list.
161- </p>
162- <pre>=: list_type none
163-# Hi my name is Bob!
164-I like to sing. Dance. And I like to have fun fun fun!</pre>
165- <p>
166- Whatever is inside the <code>_readme</code> file will get rendered (as a list) right above your
167- blog posts. Neat!
168- </p>
169- </section>
170-
171- <section id="blog-layout">
172- <h2 class="text-xl">
173- <a href="#blog-layout" rel="nofollow noopener">#</a>
174- How can I change the layout of my blog?
175- </h2>
176- <p>
177- Inside the <code>_header.txt</code> metadata file, there's a variable <code>layout</code>
178- option that will change the layout of your blog index page.
179- </p>
180- <p>Currently supported options</p>
181- <ul>
182- <li>default</li>
183- <li>aside</li>
184- </ul>
185- </section>
186-
187- <section id="blog-url">
188- <h2 class="text-xl">
189- <a href="#blog-url" rel="nofollow noopener">#</a>
190- What is my blog URL?
191- </h2>
192- <pre>https://{username}.{{.Site.Domain}}</pre>
193- </section>
194-
195- <section id="continuous-deployment">
196- <h2 class="text-xl">
197- <a href="#continuous-deployment" rel="nofollow noopener">#</a>
198- How can I automatically publish my post?
199- </h2>
200- <p>
201- There is a github action that we built to make it easy to publish your blog automatically.
202- </p>
203- <ul>
204- <li>
205- <a href="https://github.com/picosh/scp-publish-action">github action repo</a>
206- </li>
207- <li>
208- <a href="https://github.com/neurosnap/lists-official-blog/blob/main/.github/workflows/publish.yml">example workflow</a>
209- </li>
210- </ul>
211- <p>
212- A user also created a systemd task to automatically publish new posts. <a href="https://github.com/neurosnap/lists.sh/discussions/24">Check out this github discussion for more details.</a>
213- </p>
214- </section>
215-
216- <section id="multiple-accounts">
217- <h2 class="text-xl">
218- <a href="#multiple-accounts" rel="nofollow noopener">#</a>
219- Can I create multiple accounts?
220- </h2>
221- <p>
222- Yes! You can either a) create a new keypair and use that for authentication
223- or b) use the same keypair and ssh into our CMS using our special username
224- <code>ssh new@{{.Site.Domain}}</code>.
225- </p>
226- <p>
227- Please note that if you use the same keypair for multiple accounts, you will need to
228- always specify the user when logging into our CMS.
229- </p>
230- </section>
231-</main>
232-{{template "marketing-footer" .}}
233-{{end}}
1@@ -2,13 +2,5 @@
2 <footer>
3 <hr />
4 <p class="font-italic">Built and maintained by <a href="https://pico.sh">pico.sh</a>.</p>
5- <div>
6- <a href="/">home</a> |
7- <a href="/spec">spec</a> |
8- <a href="/ops">ops</a> |
9- <a href="/help">help</a> |
10- <a href="/rss">rss</a> |
11- <a href="https://github.com/picosh/pico">source</a>
12- </div>
13 </footer>
14 {{end}}
+0,
-155
1@@ -1,155 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}{{.Site.Domain}} -- a microblog for lists{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="a microblog for lists" />
8-
9-<meta property="og:type" content="website">
10-<meta property="og:site_name" content="{{.Site.Domain}}">
11-<meta property="og:url" content="https://{{.Site.Domain}}">
12-<meta property="og:title" content="{{.Site.Domain}}">
13-<meta property="og:description" content="a microblog for lists">
14-
15-<meta name="twitter:card" content="summary" />
16-<meta property="twitter:url" content="https://{{.Site.Domain}}">
17-<meta property="twitter:title" content="{{.Site.Domain}}">
18-<meta property="twitter:description" content="a microblog for lists">
19-<meta name="twitter:image" content="https://{{.Site.Domain}}/card.png" />
20-<meta name="twitter:image:src" content="https://{{.Site.Domain}}/card.png" />
21-
22-<meta property="og:image:width" content="300" />
23-<meta property="og:image:height" content="300" />
24-<meta itemprop="image" content="https://{{.Site.Domain}}/card.png" />
25-<meta property="og:image" content="https://{{.Site.Domain}}/card.png" />
26-{{end}}
27-
28-{{define "attrs"}}{{end}}
29-
30-{{define "body"}}
31-<header class="text-center">
32- <h1 class="text-2xl font-bold">{{.Site.Domain}}</h1>
33- <p class="text-lg">A microblog for lists</p>
34- <p class="text-lg"><a href="/read">discover</a> some interesting lists</p>
35- <hr />
36-</header>
37-
38-<main>
39- <section>
40- <h2 class="text-lg font-bold">Examples</h2>
41- <p>
42- <a href="//hey.{{.Site.Domain}}">official blog</a> |
43- <a href="https://github.com/picosh/lists-official-blog">blog source</a>
44- </p>
45- </section>
46-
47- <section>
48- <h2 class="text-lg font-bold">Create your account with Public-Key Cryptography</h2>
49- <p>We don't want your email address.</p>
50- <p>To get started, simply ssh into our content management system:</p>
51- <pre>ssh new@{{.Site.Domain}}</pre>
52- <div class="text-sm font-italic note">
53- note: <code>new</code> is a special username that will always send you to account
54- creation.
55- </div>
56- <div class="text-sm font-italic note">
57- note: getting permission denied? <a href="/help#permission-denied">read this</a>
58- </div>
59- <p>
60- After that, just set a username and you're ready to start writing! When you SSH
61- again, use your username that you set in the CMS.
62- </p>
63- </section>
64-
65- <section>
66- <h2 class="text-lg font-bold">You control the source files</h2>
67- <p>Create lists using your favorite editor in plain text files.</p>
68- <code>~/blog/days-in-week.txt</code>
69- <pre>Sunday
70-Monday
71-Tuesday
72-Wednesday
73-Thursday
74-Friday
75-Saturday</pre>
76- </section>
77-
78- <section>
79- <h2 class="text-lg font-bold">Publish your posts with one command</h2>
80- <p>
81- When your post is ready to be published, copy the file to our server with a familiar
82- command:
83- </p>
84- <pre>scp ~/blog/*.txt {{.Site.Domain}}:/</pre>
85- <p>We'll either create or update the lists for you.</p>
86- </section>
87-
88- <section>
89- <h2 class="text-lg font-bold">Terminal workflow without installation</h2>
90- <p>
91- Since we are leveraging tools you already have on your computer
92- (<code>ssh</code> and <code>scp</code>), there is nothing to install.
93- </p>
94- <p>
95- This provides the convenience of a web app, but from inside your terminal!
96- </p>
97- </section>
98-
99- <section>
100- <h2 class="text-lg font-bold">Plain text format</h2>
101- <p>A simple specification that is flexible and with no frills.</p>
102- <p><a href="/spec">specification</a></p>
103- </section>
104-
105- <section>
106- <h2 class="text-lg font-bold">Features</h2>
107- <ul>
108- <li>Just lists</li>
109- <li>Looks great on any device</li>
110- <li>Bring your own editor</li>
111- <li>You control the source files</li>
112- <li>Terminal workflow with no installation</li>
113- <li>Public-key based authentication</li>
114- <li>No ads, zero browser-based tracking</li>
115- <li>No platform lock-in</li>
116- <li>No javascript</li>
117- <li>Subscriptions via RSS</li>
118- <li>Not a platform for todos</li>
119- <li>Minimalist design</li>
120- <li>100% open source</li>
121- </ul>
122- </section>
123-
124- <section>
125- <h2 class="text-lg font-bold">Philosophy</h2>
126- <p>
127- I love writing lists. I think restricting writing to a set of lists can really
128- help improve clarity in thought. The goal of this blogging platform is to make it
129- simple to use the tools you love to write and publish lists. There is no installation,
130- signup is as easy as SSH'ing into our CMS, and publishing content is as easy as
131- copying files to our server.
132- </p>
133- <p>
134- Another goal of this microblog platform is to satisfy my own needs. I like to
135- write and share lists with people because I find it's one of the best way to disseminate
136- knowledge. Whether it's a list of links or a list of paragraphs, writing in lists is
137- very satisfying and I welcome you to explore it on this site!
138- </p>
139- <p>
140- Other blogging platforms support writing lists, but they don't
141- <span class="font-bold">emphasize</span> them. Writing lists is pretty popular
142- on Twitter, but discoverability is terrible. Other blogging platforms focus on prose,
143- but there really is nothing out there catered specifically for lists ... until now.
144- </p>
145- </section>
146-
147- <section>
148- <h2 class="text-lg font-bold">Roadmap</h2>
149- <ol>
150- <li>Feature complete?</li>
151- </ol>
152- </section>
153-</main>
154-
155-{{template "marketing-footer" .}}
156-{{end}}
+0,
-147
1@@ -1,147 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}operations -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} operations" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Operations</h1>
15- <ul>
16- <li><a href="/privacy">privacy</a></li>
17- <li><a href="/transparency">transparency</a></li>
18- </ul>
19-</header>
20-<main>
21- <section>
22- <h2 class="text-xl">Purpose</h2>
23- <p>
24- {{.Site.Domain}} exists to allow people to create and share their lists
25- without the need to set up their own server or be part of a platform
26- that shows ads or tracks its users.
27- </p>
28- </section>
29- <section>
30- <h2 class="text-xl">Ethics</h2>
31- <p>We are committed to:</p>
32- <ul>
33- <li>No browser-based tracking of visitor behavior.</li>
34- <li>No attempt to identify users.</li>
35- <li>Never sell any user or visitor data.</li>
36- <li>No ads — ever.</li>
37- </ul>
38- </section>
39- <section>
40- <h2 class="text-xl">Code of Content Publication</h2>
41- <p>
42- Content in {{.Site.Domain}} blogs is unfiltered and unmonitored. Users are free to publish any
43- combination of words and pixels except for: content of animosity or disparagement of an
44- individual or a group on account of a group characteristic such as race, color, national
45- origin, sex, disability, religion, or sexual orientation, which will be taken down
46- immediately.
47- </p>
48- <p>
49- If one notices something along those lines in a blog please let us know at
50- <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>.
51- </p>
52- </section>
53- <section>
54- <h2 class="text-xl">Liability</h2>
55- <p>
56- The user expressly understands and agrees that Eric Bower, the operator of this website
57- shall not be liable, in law or in equity, to them or to any third party for any direct,
58- indirect, incidental, lost profits, special, consequential, punitive or exemplary damages.
59- </p>
60- </section>
61- <section>
62- <h2 class="text-xl">Analytics</h2>
63- <p>
64- We are committed to zero browser-based tracking or trying to identify visitors. This
65- means we do not try to understand the user based on cookies or IP address. We do not
66- store personally identifiable information.
67- </p>
68- <p>
69- However, in order to provide a better service, we do have some analytics on posts.
70- List of metrics we track for posts:
71- </p>
72- <ul>
73- <li>anonymous view counts</li>
74- </ul>
75- <p>
76- We might also inspect the headers of HTTP requests to determine some tertiary information
77- about the request. For example we might inspect the <code>User-Agent</code> or
78- <code>Referer</code> to filter out requests from bots.
79- </p>
80- </section>
81- <section>
82- <h2 class="text-xl">Account Terms</h2>
83- <p>
84- <ul>
85- <li>
86- The user is responsible for all content posted and all actions performed with
87- their account.
88- </li>
89- <li>
90- We reserve the right to disable or delete a user's account for any reason at
91- any time. We have this clause because, statistically speaking, there will be
92- people trying to do something nefarious.
93- </li>
94- </ul>
95- </p>
96- </section>
97- <section>
98- <h2 class="text-xl">Service Availability</h2>
99- <p>
100- We provide the {{.Site.Domain}} service on an "as is" and "as available" basis. We do not offer
101- service-level agreements but do take uptime seriously.
102- </p>
103- </section>
104- <section>
105- <h2 class="text-xl">Contact and Support</h2>
106- <p>
107- Email us at <a href="mailto:support@{{.Site.Domain}}">support@{{.Site.Domain}}</a>
108- with any questions.
109- </p>
110- </section>
111- <section>
112- <h2 class="text-xl">Acknowledgments</h2>
113- <p>
114- {{.Site.Domain}} was inspired by <a href="https://mataroa.blog">Mataroa Blog</a>
115- and <a href="https://bearblog.dev/">Bear Blog</a>.
116- </p>
117- <p>
118- {{.Site.Domain}} is built with many open source technologies.
119- </p>
120- <p>
121- In particular we would like to thank:
122- </p>
123- <ul>
124- <li>
125- <span>The </span>
126- <a href="https://charm.sh">charm.sh</a>
127- <span> community</span>
128- </li>
129- <li>
130- <span>The </span>
131- <a href="https://go.dev">golang</a>
132- <span> community</span>
133- </li>
134- <li>
135- <span>The </span>
136- <a href="https://www.postgresql.org/">postgresql</a>
137- <span> community</span>
138- </li>
139- <li>
140- <span>The </span>
141- <a href="https://github.com/caddyserver/caddy">caddy</a>
142- <span> community</span>
143- </li>
144- </ul>
145- </section>
146-</main>
147-{{template "marketing-footer" .}}
148-{{end}}
+0,
-54
1@@ -1,54 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}privacy -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} privacy policy" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Privacy</h1>
15- <p>Details on our privacy and security approach.</p>
16-</header>
17-<main>
18- <section>
19- <h2 class="text-xl">Account Data</h2>
20- <p>
21- In order to have a functional account at {{.Site.Domain}}, we need to store
22- your public key. That is the only piece of information we record for a user.
23- </p>
24- <p>
25- Because we use public-key cryptography, our security posture is a battle-tested
26- and proven technique for authentication.
27- </p>
28- </section>
29-
30- <section>
31- <h2 class="text-xl">Third parties</h2>
32- <p>
33- We have a strong commitment to never share any user data with any third-parties.
34- </p>
35- </section>
36-
37- <section>
38- <h2 class="text-xl">Service Providers</h2>
39- <ul>
40- <li>
41- <span>We host our server on </span>
42- <a href="https://www.oracle.com/cloud/">oracle cloud</a>
43- </li>
44- </ul>
45- </section>
46-
47- <section>
48- <h2 class="text-xl">Cookies</h2>
49- <p>
50- We do not use any cookies, not even account authentication.
51- </p>
52- </section>
53-</main>
54-{{template "marketing-footer" .}}
55-{{end}}
+5,
-2
1@@ -10,8 +10,11 @@
2
3 {{define "body"}}
4 <header class="text-center">
5- <h1 class="text-2xl font-bold">read</h1>
6- <p class="text-lg">recently updated lists</p>
7+ <h1 class="text-2xl font-bold">lists.sh</h1>
8+ <p class="text-lg">A microblog for lists</p>
9+ <div>
10+ <a href="https://pico.sh/getting-started" class="btn-link mt inline-block">GET STARTED</a>
11+ </div>
12 <hr />
13 </header>
14 <main>
+0,
-224
1@@ -1,224 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}specification -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="a specification for lists" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Plain text list</h1>
15- <h2 class="text-xl">Speculative specification</h2>
16- <dl>
17- <dt>Version</dt>
18- <dd>2022.08.05.dev</dd>
19-
20- <dt>Status</dt>
21- <dd>Draft</dd>
22-
23- <dt>Author</dt>
24- <dd>Eric Bower</dd>
25- </dl>
26-</header>
27-<main>
28- <section id="overview">
29- <p>
30- The goal of this specification is to understand how we render plain text lists.
31- The overall design of this format is to be easy to parse and render.
32- </p>
33-
34- <p>
35- The format is line-oriented, and a satisfactory rendering can be achieved with a single
36- pass of a document, processing each line independently. As per gopher, links can only be
37- displayed one per line, encouraging neat, list-like structure.
38- </p>
39-
40- <p>
41- Feedback on any part of this is extremely welcome, please email
42- <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>.
43- </p>
44-
45- <p>
46- The source code for our parser can be found
47- <a href="https://github.com/picosh/pico/tree/main/item/lists/parser.go">here</a>.
48- </p>
49- </section>
50-
51- <section id="parameters">
52- <h2 class="text-xl">
53- <a href="#parameters" rel="nofollow noopener">#</a>
54- Parameters
55- </h2>
56- <p>
57- As a subtype of the top-level media type "text", "text/plain" inherits the "charset"
58- parameter defined in <a href="https://datatracker.ietf.org/doc/html/rfc2046#section-4.1">RFC 2046</a>.
59- The default value of "charset" is "UTF-8" for "text" content.
60- </p>
61- </section>
62-
63- <section id="line-orientation">
64- <h2 class="text-xl">
65- <a href="#line-orientation" rel="nofollow noopener">#</a>
66- Line orientation
67- </h2>
68- <p>
69- As mentioned, the text format is line-oriented. Each line of a document has a single
70- "line type". It is possible to unambiguously determine a line's type purely by
71- inspecting its first (3) characters. A line's type determines the manner in which it
72- should be presented to the user. Any details of presentation or rendering associated
73- with a particular line type are strictly limited in scope to that individual line.
74- </p>
75- </section>
76-
77- <section id="file-extensions">
78- <h2 class="text-xl">
79- <a href="#file-extensions" rel="nofollow noopener">#</a>
80- File extension
81- </h2>
82- <p>
83- {{.Site.Domain}} only supports the <code>.txt</code> file extension and will
84- ignore all other file extensions.
85- </p>
86- </section>
87-
88- <section id="list-item">
89- <h2 class="text-xl">
90- <a href="#list-item" rel="nofollow noopener">#</a>
91- List item
92- </h2>
93- <p>
94- List items are separated by newline characters <code>\n</code>.
95- Each list item is on its own line. A list item does not require any special formatting.
96- A list item can contain as much text as it wants. We encourage soft wrapping for readability
97- in your editor of choice. Hard wrapping is not permitted as it will create a new list item.
98- </p>
99- <p>
100- Empty lines will be completely removed and not rendered to the end user.
101- </p>
102- </section>
103-
104- <section id="hyperlinks">
105- <h2 class="text-xl">
106- <a href="#hyperlinks" rel="nofollow noopener">#</a>
107- Hyperlinks
108- </h2>
109- <p>
110- Hyperlinks are denoted by the prefix <code>=></code>. The following text should then be
111- the hyperlink.
112- </p>
113- <pre>=> https://{{.Site.Domain}}</pre>
114- <p>Optionally you can supply the hyperlink text immediately following the link.</p>
115- <pre>=> https://{{.Site.Domain}} microblog for lists</pre>
116- </section>
117-
118- <section id="nested-lists">
119- <h2 class="text-xl">
120- <a href="#nested-lists" rel="nofollow noopener">#</a>
121- Nested lists
122- </h2>
123- <p>
124- Users can create nested lists. Tabbing a list will nest it under the list item
125- directly above it. Both tab character <code>\t</code> or whitespace as tabs are permitted.
126- Do not mix tabs and spaces because the nesting will yield unexpected results.
127- </p>
128- <pre>first item
129- second item
130- third item
131-last item</pre>
132- </section>
133-
134- <section id="images">
135- <h2 class="text-xl">
136- <a href="#images" rel="nofollow noopener">#</a>
137- Images
138- </h2>
139- <p>
140- List items can be represented as images by prefixing the line with <code>=<</code>.
141- </p>
142- <pre>=< https://i.imgur.com/iXMNUN5.jpg</pre>
143- <p>Optionally you can supply the image alt text immediately following the link.</p>
144- <pre>=< https://i.imgur.com/iXMNUN5.jpg I use arch, btw</pre>
145- </section>
146-
147- <section id="headers">
148- <h2 class="text-xl">
149- <a href="#headers" rel="nofollow noopener">#</a>
150- Headers
151- </h2>
152- <p>
153- List items can be represented as headers. We support two headers currently. Headers
154- will end the previous list and then create a new one after it. This allows a single
155- document to contain multiple lists.
156- </p>
157- <pre># Header One
158-## Header Two</pre>
159- </section>
160-
161- <section id="blockquotes">
162- <h2 class="text-xl">
163- <a href="#blockquotes" rel="nofollow noopener">#</a>
164- Blockquotes
165- </h2>
166- <p>
167- List items can be represented as blockquotes.
168- </p>
169- <pre>> This is a blockquote.</pre>
170- </section>
171-
172- <section id="preformatted">
173- <h2 class="text-xl">
174- <a href="#preformatted" rel="nofollow noopener">#</a>
175- Preformatted
176- </h2>
177- <p>
178- List items can be represented as preformatted text where newline characters are not
179- considered part of new list items. They can be represented by prefixing the line with
180- <code>```</code>.
181- </p>
182- <pre>```
183-#!/usr/bin/env bash
184-
185-set -x
186-
187-echo "this is a preformatted list item!
188-```</pre>
189- <p>
190- You must also close the preformatted text with another <code>```</code> on its own line. The
191- next example with NOT work.
192- </p>
193- <pre>```
194-#!/usr/bin/env bash
195-
196-echo "This will not render properly"```</pre>
197- </section>
198-
199- <section id="variables">
200- <h2 class="text-xl">
201- <a href="#variables" rel="nofollow noopener">#</a>
202- Variables
203- </h2>
204- <p>
205- Variables allow us to store metadata within our system. Variables are list items with
206- key value pairs denoted by <code>=:</code> followed by the key, a whitespace character,
207- and then the value.
208- </p>
209- <pre>=: publish_at 2022-04-20</pre>
210- <p>These variables will not be rendered to the user inside the list.</p>
211- <h3 class="text-lg">List of available variables:</h3>
212- <ul>
213- <li><code>title</code> (custom title not dependent on filename)</li>
214- <li><code>description</code> (what is the purpose of this list?)</li>
215- <li><code>publish_at</code> (format must be <code>YYYY-MM-DD</code>)</li>
216- <li><code>tags</code> (format must be comma delimited: <code>feature, announcement</code>)</li>
217- <li>
218- <code>list_type</code> (customize bullets; value gets sent directly to css property
219- <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/list-style-type">list-style-type</a>)
220- </li>
221- </ul>
222- </section>
223-</main>
224-{{template "marketing-footer" .}}
225-{{end}}
+0,
-59
1@@ -1,59 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}transparency -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="full transparency of analytics and cost at {{.Site.Domain}}" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Transparency</h1>
15- <hr />
16-</header>
17-<main>
18- <section>
19- <h2 class="text-xl">Analytics</h2>
20- <p>
21- Here are some interesting stats on usage.
22- </p>
23-
24- <article>
25- <h2 class="text-lg">Total users</h2>
26- <div>{{.Analytics.TotalUsers}}</div>
27- </article>
28-
29- <article>
30- <h2 class="text-lg">New users in the last month</h2>
31- <div>{{.Analytics.UsersLastMonth}}</div>
32- </article>
33-
34- <article>
35- <h2 class="text-lg">Total posts</h2>
36- <div>{{.Analytics.TotalPosts}}</div>
37- </article>
38-
39- <article>
40- <h2 class="text-lg">New posts in the last month</h2>
41- <div>{{.Analytics.PostsLastMonth}}</div>
42- </article>
43-
44- <article>
45- <h2 class="text-lg">Users with at least one post</h2>
46- <div>{{.Analytics.UsersWithPost}}</div>
47- </article>
48- </section>
49-
50- <section>
51- <h2 class="text-xl">Service maintenance costs</h2>
52- <ul>
53- <li>Domain name $3.25/mo</li>
54- <li>Server $0.00/mo</li>
55- <li>Programmer $0.00/mo</li>
56- </ul>
57- </section>
58-</main>
59-{{template "marketing-footer" .}}
60-{{end}}
+23,
-0
1@@ -17,6 +17,7 @@
2
3 @media (prefers-color-scheme: light) {
4 :root {
5+ --main-hue: 250;
6 --white: #6a737d;
7 --code: #fff8d3;
8 --code-border: #f0d547;
9@@ -34,6 +35,7 @@
10
11 @media (prefers-color-scheme: dark) {
12 :root {
13+ --main-hue: 250;
14 --white: #f2f2f2;
15 --code: #414558;
16 --code-border: #252525;
17@@ -271,6 +273,23 @@ figure {
18 margin: 0;
19 }
20
21+.btn-link {
22+ border: 3px solid hsl(var(--main-hue), 92%, 66%);
23+ background-color: hsl(var(--main-hue), 92%, 66%);
24+ padding: 0.5rem 1rem;
25+ border-radius: 0.25rem;
26+ box-shadow: 0px 1px 2px 0px black;
27+ color: var(--white);
28+ text-decoration: none;
29+ font-weight: bold;
30+}
31+
32+.btn-link:visited,
33+.btn-link:visited:hover,
34+.btn-link:hover {
35+ color: var(--white);
36+}
37+
38 .post-date {
39 width: 130px;
40 }
41@@ -319,6 +338,10 @@ figure {
42 display: inline;
43 }
44
45+.inline-block {
46+ display: inline-block;
47+}
48+
49 .flex {
50 display: flex;
51 }
+0,
-39
1@@ -274,40 +274,6 @@ func postHandlerRaw(w http.ResponseWriter, r *http.Request) {
2 }
3 }
4
5-func transparencyHandler(w http.ResponseWriter, r *http.Request) {
6- dbpool := shared.GetDB(r)
7- logger := shared.GetLogger(r)
8- cfg := shared.GetCfg(r)
9-
10- analytics, err := dbpool.FindSiteAnalytics(cfg.Space)
11- if err != nil {
12- logger.Error(err)
13- http.Error(w, err.Error(), http.StatusInternalServerError)
14- return
15- }
16-
17- ts, err := template.ParseFiles(
18- cfg.StaticPath("html/transparency.page.tmpl"),
19- cfg.StaticPath("html/footer.partial.tmpl"),
20- cfg.StaticPath("html/marketing-footer.partial.tmpl"),
21- cfg.StaticPath("html/base.layout.tmpl"),
22- )
23-
24- if err != nil {
25- http.Error(w, err.Error(), http.StatusInternalServerError)
26- }
27-
28- data := TransparencyPageData{
29- Site: *cfg.GetSiteData(),
30- Analytics: analytics,
31- }
32- err = ts.Execute(w, data)
33- if err != nil {
34- logger.Error(err)
35- http.Error(w, err.Error(), http.StatusInternalServerError)
36- }
37-}
38-
39 func serveFile(file string, contentType string) http.HandlerFunc {
40 return func(w http.ResponseWriter, r *http.Request) {
41 logger := shared.GetLogger(r)
42@@ -345,11 +311,6 @@ func createStaticRoutes() []shared.Route {
43 func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
44 routes := []shared.Route{
45 shared.NewRoute("GET", "/", shared.CreatePageHandler("html/marketing.page.tmpl")),
46- shared.NewRoute("GET", "/spec", shared.CreatePageHandler("html/spec.page.tmpl")),
47- shared.NewRoute("GET", "/ops", shared.CreatePageHandler("html/ops.page.tmpl")),
48- shared.NewRoute("GET", "/privacy", shared.CreatePageHandler("html/privacy.page.tmpl")),
49- shared.NewRoute("GET", "/help", shared.CreatePageHandler("html/help.page.tmpl")),
50- shared.NewRoute("GET", "/transparency", transparencyHandler),
51 shared.NewRoute("GET", "/check", shared.CheckHandler),
52 }
53
+0,
-154
1@@ -1,154 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}help -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="questions and answers" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Need help?</h1>
15- <p>Here are some common questions on using this platform that we would like to answer.</p>
16-</header>
17-<main>
18- <section id="permission-denied">
19- <h2 class="text-xl">
20- <a href="#permission-denied" rel="nofollow noopener">#</a>
21- I get a permission denied when trying to SSH
22- </h2>
23- <p>
24- Unfortunately SHA-2 RSA keys are <strong>not</strong> currently supported.
25- </p>
26- <p>
27- Unfortunately, due to a shortcoming in Go’s x/crypto/ssh package, we do
28- not currently support access via new SSH RSA keys: only the old SHA-1 ones will work.
29- Until we sort this out you’ll either need an SHA-1 RSA key or a key with another
30- algorithm, e.g. Ed25519. Not sure what type of keys you have? You can check with the
31- following:
32- </p>
33- <pre>$ find ~/.ssh/id_*.pub -exec ssh-keygen -l -f {} \;</pre>
34- <p>If you’re curious about the inner workings of this problem have a look at:</p>
35- <ul>
36- <li><a href="https://github.com/golang/go/issues/37278">golang/go#37278</a></li>
37- <li><a href="https://go-review.googlesource.com/c/crypto/+/220037">go-review</a></li>
38- <li><a href="https://github.com/golang/crypto/pull/197">golang/crypto#197</a></li>
39- </ul>
40- </section>
41-
42- <section id="ssh-key">
43- <h2 class="text-xl">
44- <a href="#ssh-key" rel="nofollow noopener">#</a>
45- Generating a new SSH key
46- </h2>
47- <p>
48- <a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">Github reference</a>
49- </p>
50- <pre>ssh-keygen -t ed25519 -C "your_email@example.com"</pre>
51- <ol>
52- <li>When you're prompted to "Enter a file in which to save the key," press Enter. This accepts the default file location.</li>
53- <li>At the prompt, type a secure passphrase.</li>
54- </ol>
55- </section>
56-
57- <section id="paste-update">
58- <h2 class="text-xl">
59- <a href="#paste-update" rel="nofollow noopener">#</a>
60- How do I update a paste?
61- </h2>
62- <p>
63- Updating a paste requires that you update the source document and then run the <code>scp</code>
64- command again. If the filename remains the same, then the paste will be updated.
65- </p>
66- </section>
67-
68- <section id="paste-delete">
69- <h2 class="text-xl">
70- <a href="#paste-delete" rel="nofollow noopener">#</a>
71- How do I delete a paste?
72- </h2>
73- <p>
74- Because <code>scp</code> does not natively support deleting files, I didn't want to bake
75- that behavior into my ssh server.
76- </p>
77-
78- <p>
79- However, if a user wants to delete a paste they can delete the contents of the file and
80- then upload it to our server. If the file contains 0 bytes, we will remove the post.
81- For example, if you want to delete <code>delete.txt</code> you could:
82- </p>
83-
84- <pre>
85-cp /dev/null delete.txt
86-scp ./delete.txt {{.Site.Domain}}:/</pre>
87-
88- <p>
89- Alternatively, you can go to <code>ssh {{.Site.Domain}}</code> and select "Manage posts."
90- Then you can highlight the paste you want to delete and then press "X." It will ask for
91- confirmation before actually removing the paste.
92- </p>
93- </section>
94-
95- <section id="multiple-accounts">
96- <h2 class="text-xl">
97- <a href="#multiple-accounts" rel="nofollow noopener">#</a>
98- Can I create multiple accounts?
99- </h2>
100- <p>
101- Yes! You can either a) create a new keypair and use that for authentication
102- or b) use the same keypair and ssh into our CMS using our special username
103- <code>ssh new@{{.Site.Domain}}</code>.
104- </p>
105- <p>
106- Please note that if you use the same keypair for multiple accounts, you will need to
107- always specify the user when logging into our CMS.
108- </p>
109- </section>
110-
111- <section id="pipe">
112- <h2 class="text-xl">
113- <a href="#pipe" rel="nofollow noopener">#</a>
114- Can I pipe my paste?
115- </h2>
116- <p>
117- Yes!
118- </p>
119- <pre>echo "foobar" | ssh pastes.sh</pre>
120- <pre>echo "foobar" | ssh pastes.sh FILENAME</pre>
121- <pre># if the tty warning annoys you
122-echo "foobar" | ssh -T pastes.sh</pre>
123- </section>
124-
125- <section id="expires">
126- <h2 class="text-xl">
127- <a href="#expires" rel="nofollow noopener">#</a>
128- Can I set the expiration date to a paste?
129- </h2>
130- <p>
131- Yes. The default expiration date for a paste is 90 days.
132- We do allow the user to set the paste to never expire.
133- We also allow custom <a href="https://pkg.go.dev/time#ParseDuration">duration</a>
134- or <a href="https://pkg.go.dev/github.com/araddon/dateparse#ParseStrict">timestamp</a>.
135- </p>
136- <pre>echo "foobar" | ssh pastes.sh FILENAME expires=false</pre>
137- <pre>echo "foobar" | ssh pastes.sh FILENAME expires=2023-12-12</pre>
138- <pre>echo "foobar" | ssh pastes.sh FILENAME expires=1h</pre>
139- </section>
140-
141- <section id="unlisted">
142- <h2 class="text-xl">
143- <a href="#unlisted" rel="nofollow noopener">#</a>
144- Can I hide pastes from my landing page?
145- </h2>
146- <p>
147- Yes. Unlisted in this context means it does not show up on
148- your user landing page where we show all of your pastes.
149- In this case, yes, you can "hide" it using a pipe command.
150- </p>
151- <pre>echo "foobar" | ssh pastes.sh FILENAME hidden=true</pre>
152- </section>
153-</main>
154-{{template "marketing-footer" .}}
155-{{end}}
1@@ -2,11 +2,5 @@
2 <footer>
3 <hr />
4 <p class="font-italic">Built and maintained by <a href="https://pico.sh">pico.sh</a>.</p>
5- <div>
6- <a href="/">home</a> |
7- <a href="/ops">ops</a> |
8- <a href="/help">help</a> |
9- <a href="https://github.com/picosh/pico">source</a>
10- </div>
11 </footer>
12 {{end}}
+3,
-99
1@@ -30,106 +30,10 @@
2 <header class="text-center">
3 <h1 class="text-2xl font-bold">{{.Site.Domain}}</h1>
4 <p class="text-lg">a pastebin for hackers</p>
5- <hr />
6+ <div>
7+ <a href="https://pico.sh/getting-started" class="btn-link mt inline-block">GET STARTED</a>
8+ </div>
9 </header>
10
11-<main>
12- <section>
13- <h2 class="text-lg font-bold">Create your account with Public-Key Cryptography</h2>
14- <p>We don't want your email address.</p>
15- <p>To get started, simply ssh into our content management system:</p>
16- <pre>ssh new@{{.Site.Domain}}</pre>
17- <div class="text-sm font-italic note">
18- note: <code>new</code> is a special username that will always send you to account
19- creation, even with multiple accounts associated with your key-pair.
20- </div>
21- <div class="text-sm font-italic note">
22- note: getting permission denied? <a href="/help#permission-denied">read this</a>
23- </div>
24- <p>
25- After that, just set a username and you're ready to start writing! When you SSH
26- again, use your username that you set in the CMS.
27- </p>
28- </section>
29-
30- <section>
31- <h2 class="text-lg font-bold">Publish your pastes with one command</h2>
32- <p>
33- When your paste is ready to be published, copy the file to our server with a familiar
34- command:
35- </p>
36- <pre>scp ./changes.patch {{.Site.Domain}}:/</pre>
37- <p>We'll return the URL back to you so you never have to leave the terminal!</p>
38- </section>
39-
40- <section>
41- <h2 class="text-lg font-bold">Pipe Support</h2>
42-
43- <pre>echo "foobar" | ssh pastes.sh</pre>
44- <pre>echo "foobar" | ssh pastes.sh FILENAME</pre>
45- <pre># if the tty warning annoys you
46-echo "foobar" | ssh -T pastes.sh</pre>
47- </section>
48-
49- <section>
50- <h2 class="text-lg font-bold">Terminal workflow without installation</h2>
51- <p>
52- Since we are leveraging tools you already have on your computer
53- (<code>ssh</code>, <code>scp</code>, and <code>rsync</code>), there is nothing to install.
54- </p>
55- <p>
56- This provides the convenience of a web app, but from inside your terminal!
57- </p>
58- </section>
59-
60- <section>
61- <h2 class="text-lg font-bold">Upload using <code>rsync</code></h2>
62- <pre>rsync hello-world.diff {{.Site.Domain}}:/</pre>
63- </section>
64-
65- <section>
66- <h2 class="text-lg font-bold">Use <code>sftp</code> to manage your pastes</h2>
67- <p>
68- FTP is back in style. List, upload, and delete pastes via <code>sftp</code>:
69- </p>
70- <pre>sftp {{.Site.Domain}}
71-sftp> ls
72-hello-world.diff
73-sftp> rm hello-world.diff
74-sftp> put hello-world.diff</pre>
75- </section>
76-
77- <section>
78- <h2 class="text-lg font-bold">Features</h2>
79- <ul>
80- <li>Pastes last <strong>90 days</strong> by default</li>
81- <li><a href="/help#expires">Ability to set custom expiration</a></li>
82- <li><a href="/help#unlisted">Ability to "hide" pastes</a></li>
83- <li>Bring your own editor</li>
84- <li>Terminal workflow with no installation</li>
85- <li>Use sftp to manage pastes</li>
86- <li>Public-key based authentication</li>
87- <li>No ads, zero tracking</li>
88- <li>No javascript</li>
89- <li>Minimalist design</li>
90- <li>100% open source</li>
91- </ul>
92- </section>
93-
94- <section>
95- <h2 class="text-lg font-bold">Read the docs</h2>
96- <p>
97- <a href="/help">docs</a>
98- </p>
99- </section>
100-
101- <section>
102- <h2 class="text-lg font-bold">Roadmap</h2>
103- <ol>
104- <li>Mobile support?</li>
105- </ol>
106- </section>
107-</main>
108-
109 {{template "marketing-footer" .}}
110 {{end}}
+0,
-126
1@@ -1,126 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}operations -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} operations" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Operations</h1>
15- <ul>
16- <li><a href="/privacy">privacy</a></li>
17- <li><a href="/transparency">transparency</a></li>
18- </ul>
19-</header>
20-<main>
21- <section>
22- <h2 class="text-xl">Purpose</h2>
23- <p>
24- {{.Site.Domain}} exists to allow people to create and share their thoughts
25- without the need to set up their own server or be part of a platform
26- that shows ads or tracks its users.
27- </p>
28- </section>
29- <section>
30- <h2 class="text-xl">Ethics</h2>
31- <p>We are committed to:</p>
32- <ul>
33- <li>No tracking of user or visitor behaviour.</li>
34- <li>Never sell any user or visitor data.</li>
35- <li>No ads — ever.</li>
36- </ul>
37- </section>
38- <section>
39- <h2 class="text-xl">Code of Content Publication</h2>
40- <p>
41- Content in {{.Site.Domain}} pastes is unfiltered and unmonitored. Users are free to publish any
42- combination of words and pixels except for: content of animosity or disparagement of an
43- individual or a group on account of a group characteristic such as race, color, national
44- origin, sex, disability, religion, or sexual orientation, which will be taken down
45- immediately.
46- </p>
47- <p>
48- If one notices something along those lines in a paste post please let us know at
49- <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>.
50- </p>
51- </section>
52- <section>
53- <h2 class="text-xl">Liability</h2>
54- <p>
55- The user expressly understands and agrees that Eric Bower and Antonio Mika, the operator of this website
56- shall not be liable, in law or in equity, to them or to any third party for any direct,
57- indirect, incidental, lost profits, special, consequential, punitive or exemplary damages.
58- </p>
59- </section>
60- <section>
61- <h2 class="text-xl">Account Terms</h2>
62- <p>
63- <ul>
64- <li>
65- The user is responsible for all content posted and all actions performed with
66- their account.
67- </li>
68- <li>
69- We reserve the right to disable or delete a user's account for any reason at
70- any time. We have this clause because, statistically speaking, there will be
71- people trying to do something nefarious.
72- </li>
73- </ul>
74- </p>
75- </section>
76- <section>
77- <h2 class="text-xl">Service Availability</h2>
78- <p>
79- We provide the {{.Site.Domain}} service on an "as is" and "as available" basis. We do not offer
80- service-level agreements but do take uptime seriously.
81- </p>
82- </section>
83- <section>
84- <h2 class="text-xl">Contact and Support</h2>
85- <p>
86- Email us at <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>
87- with any questions.
88- </p>
89- </section>
90- <section>
91- <h2 class="text-xl">Acknowledgments</h2>
92- <p>
93- {{.Site.Domain}} was inspired by <a href="https://mataroa.blog">Mataroa Blog</a>
94- and <a href="https://bearblog.dev/">Bear Blog</a>.
95- </p>
96- <p>
97- {{.Site.Domain}} is built with many open source technologies.
98- </p>
99- <p>
100- In particular we would like to thank:
101- </p>
102- <ul>
103- <li>
104- <span>The </span>
105- <a href="https://charm.sh">charm.sh</a>
106- <span> community</span>
107- </li>
108- <li>
109- <span>The </span>
110- <a href="https://go.dev">golang</a>
111- <span> community</span>
112- </li>
113- <li>
114- <span>The </span>
115- <a href="https://www.postgresql.org/">postgresql</a>
116- <span> community</span>
117- </li>
118- <li>
119- <span>The </span>
120- <a href="https://github.com/caddyserver/caddy">caddy</a>
121- <span> community</span>
122- </li>
123- </ul>
124- </section>
125-</main>
126-{{template "marketing-footer" .}}
127-{{end}}
+0,
-54
1@@ -1,54 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}privacy -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} privacy policy" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Privacy</h1>
15- <p>Details on our privacy and security approach.</p>
16-</header>
17-<main>
18- <section>
19- <h2 class="text-xl">Account Data</h2>
20- <p>
21- In order to have a functional account at {{.Site.Domain}}, we need to store
22- your public key. That is the only piece of information we record for a user.
23- </p>
24- <p>
25- Because we use public-key cryptography, our security posture is a battle-tested
26- and proven technique for authentication.
27- </p>
28- </section>
29-
30- <section>
31- <h2 class="text-xl">Third parties</h2>
32- <p>
33- We have a strong commitment to never share any user data with any third-parties.
34- </p>
35- </section>
36-
37- <section>
38- <h2 class="text-xl">Service Providers</h2>
39- <ul>
40- <li>
41- <span>We host our server on </span>
42- <a href="https://www.oracle.com/cloud/">oracle cloud</a>
43- </li>
44- </ul>
45- </section>
46-
47- <section>
48- <h2 class="text-xl">Cookies</h2>
49- <p>
50- We do not use any cookies, not even account authentication.
51- </p>
52- </section>
53-</main>
54-{{template "marketing-footer" .}}
55-{{end}}
+0,
-59
1@@ -1,59 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}transparency -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="full transparency of analytics and cost at {{.Site.Domain}}" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Transparency</h1>
15- <hr />
16-</header>
17-<main>
18- <section>
19- <h2 class="text-xl">Analytics</h2>
20- <p>
21- Here are some interesting stats on usage.
22- </p>
23-
24- <article>
25- <h2 class="text-lg">Total users</h2>
26- <div>{{.Analytics.TotalUsers}}</div>
27- </article>
28-
29- <article>
30- <h2 class="text-lg">New users in the last month</h2>
31- <div>{{.Analytics.UsersLastMonth}}</div>
32- </article>
33-
34- <article>
35- <h2 class="text-lg">Total pastes</h2>
36- <div>{{.Analytics.TotalPosts}}</div>
37- </article>
38-
39- <article>
40- <h2 class="text-lg">New pastes in the last month</h2>
41- <div>{{.Analytics.PostsLastMonth}}</div>
42- </article>
43-
44- <article>
45- <h2 class="text-lg">Users with at least one paste</h2>
46- <div>{{.Analytics.UsersWithPost}}</div>
47- </article>
48- </section>
49-
50- <section>
51- <h2 class="text-xl">Service maintenance costs</h2>
52- <ul>
53- <li>Domain name $3.25/mo</li>
54- <li>Server $0.00/mo</li>
55- <li>Programmer $0.00/mo</li>
56- </ul>
57- </section>
58-</main>
59-{{template "marketing-footer" .}}
60-{{end}}
+23,
-0
1@@ -17,6 +17,7 @@
2
3 @media (prefers-color-scheme: light) {
4 :root {
5+ --main-hue: 250;
6 --white: #6a737d;
7 --code: #fff8d3;
8 --code-border: #f0d547;
9@@ -34,6 +35,7 @@
10
11 @media (prefers-color-scheme: dark) {
12 :root {
13+ --main-hue: 250;
14 --white: #f2f2f2;
15 --code: #414558;
16 --code-border: #252525;
17@@ -271,6 +273,23 @@ figure {
18 margin: 0;
19 }
20
21+.btn-link {
22+ border: 3px solid hsl(var(--main-hue), 92%, 66%);
23+ background-color: hsl(var(--main-hue), 92%, 66%);
24+ padding: 0.5rem 1rem;
25+ border-radius: 0.25rem;
26+ box-shadow: 0px 1px 2px 0px black;
27+ color: var(--white);
28+ text-decoration: none;
29+ font-weight: bold;
30+}
31+
32+.btn-link:visited,
33+.btn-link:visited:hover,
34+.btn-link:hover {
35+ color: var(--white);
36+}
37+
38 .post-date {
39 width: 130px;
40 }
41@@ -319,6 +338,10 @@ figure {
42 display: inline;
43 }
44
45+.inline-block {
46+ display: inline-block;
47+}
48+
49 .flex {
50 display: flex;
51 }
+9,
-7
1@@ -339,11 +339,6 @@ func serveAsset(subdomain string, w http.ResponseWriter, r *http.Request) {
2 })
3 }
4
5-func marketingRequest(w http.ResponseWriter, r *http.Request) {
6- subdomain := "hey-pgs-prod"
7- serveAsset(subdomain, w, r)
8-}
9-
10 func assetRequest(w http.ResponseWriter, r *http.Request) {
11 subdomain := shared.GetSubdomain(r)
12 serveAsset(subdomain, w, r)
13@@ -374,10 +369,17 @@ func StartApiServer() {
14 }
15
16 mainRoutes := []shared.Route{
17- shared.NewRoute("GET", "/", marketingRequest),
18+ shared.NewRoute("GET", "/main.css", shared.ServeFile("main.css", "text/css")),
19+ shared.NewRoute("GET", "/card.png", shared.ServeFile("card.png", "image/png")),
20+ shared.NewRoute("GET", "/favicon-16x16.png", shared.ServeFile("favicon-16x16.png", "image/png")),
21+ shared.NewRoute("GET", "/apple-touch-icon.png", shared.ServeFile("apple-touch-icon.png", "image/png")),
22+ shared.NewRoute("GET", "/favicon.ico", shared.ServeFile("favicon.ico", "image/x-icon")),
23+ shared.NewRoute("GET", "/robots.txt", shared.ServeFile("robots.txt", "text/plain")),
24+
25+ shared.NewRoute("GET", "/", shared.CreatePageHandler("html/marketing.page.tmpl")),
26 shared.NewRoute("GET", "/check", checkHandler),
27 shared.NewRoute("GET", "/rss", rssHandler),
28- shared.NewRoute("GET", "/(.+)", marketingRequest),
29+ shared.NewRoute("GET", "/(.+)", shared.CreatePageHandler("html/marketing.page.tmpl")),
30 }
31 subdomainRoutes := []shared.Route{
32 shared.NewRoute("GET", "/", assetRequest),
+0,
-172
1@@ -1,172 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}help -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="questions and answers" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Need help?</h1>
15- <p>Here are some common questions on using this platform that we would like to answer.</p>
16-</header>
17-<main>
18- <section id="permission-denied">
19- <h2 class="text-xl">
20- <a href="#permission-denied" rel="nofollow noopener">#</a>
21- I get a permission denied when trying to SSH
22- </h2>
23- <p>
24- Unfortunately SHA-2 RSA keys are <strong>not</strong> currently supported.
25- </p>
26- <p>
27- Unfortunately, due to a shortcoming in Go’s x/crypto/ssh package, we
28- not currently support access via new SSH RSA keys: only the old SHA-1 ones will work.
29- Until we sort this out you’ll either need an SHA-1 RSA key or a key with another
30- algorithm, e.g. Ed25519. Not sure what type of keys you have? You can check with the
31- following:
32- </p>
33- <pre>$ find ~/.ssh/id_*.pub -exec ssh-keygen -l -f {} \;</pre>
34- <p>If you’re curious about the inner workings of this problem have a look at:</p>
35- <ul>
36- <li><a href="https://github.com/golang/go/issues/37278">golang/go#37278</a></li>
37- <li><a href="https://go-review.googlesource.com/c/crypto/+/220037">go-review</a></li>
38- <li><a href="https://github.com/golang/crypto/pull/197">golang/crypto#197</a></li>
39- </ul>
40- </section>
41-
42- <section id="ssh-key">
43- <h2 class="text-xl">
44- <a href="#ssh-key" rel="nofollow noopener">#</a>
45- Generating a new SSH key
46- </h2>
47- <p>
48- <a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">Github reference</a>
49- </p>
50- <pre>ssh-keygen -t ed25519 -C "your_email@example.com"</pre>
51- <ol>
52- <li>When you're prompted to "Enter a file in which to save the key," press Enter. This accepts the default file location.</li>
53- <li>At the prompt, type a secure passphrase.</li>
54- </ol>
55- </section>
56-
57- <section id="cli-commands">
58- <h2 class="text-xl">
59- <a href="#cli-commands" rel="nofollow noopener">#</a>
60- CLI Commands
61- </h2>
62- <p>
63- Our CLI commands are currently in active development so the list of available commands are changing.
64- </p>
65- <p>The best way to learn about all the commands we support is via an SSH command:</p>
66- <pre>ssh {{.Site.Domain}} help</pre>
67- <p>
68- Having said that, we do want to demonstrate the power of {{.Site.Domain}} by discussing design goals.
69- All of our SSH commands are safe-by-default. Meaning, they never mutate server state by default.
70- This provides users an opportunity to experiment with our commands to see how they work. In order to
71- actually trigger server mutations, every command must be appended with <code>--write</code>.
72- </p>
73- <p>
74- Further, we want to make sure users are able to manage their static sites exclusively from SSH commands.
75- Below is list of features we support via SSH commands:
76- </p>
77- <ul>
78- <li>Storage usage stats: <code>ssh <user>@{{.Site.Domain}} stats</code></li>
79- <li>List all projects (and their links): <code>ssh <user>@{{.Site.Domain}} ls</code></li>
80- <li>List all project dependencies: <code>ssh <user>@{{.Site.Domain}} depends project-x</code></li>
81- <li>Link a project: <code>ssh <user>@{{.Site.Domain}} link project-x project-y</code></li>
82- <li>Unlink a project: <code>ssh <user>@{{.Site.Domain}} unlink project-x</code></li>
83- <li>Delete a project (and all assets): <code>ssh <user>@{{.Site.Domain}} rm project-x</code></li>
84- <li>Delete all projects matching a prefix (except projects that have linked projects): <code>ssh <user>@{{.Site.Domain}} prune prefix</code></li>
85- </ul>
86- </section>
87-
88- <section id="file-types">
89- <h2 class="text-xl">
90- <a href="#file-types" rel="nofollow noopener">#</a>
91- What file types are supported?
92- </h2>
93- <ul>
94- <li>html</li>
95- <li>htm</li>
96- <li>css</li>
97- <li>js</li>
98- <li>jpg</li>
99- <li>png</li>
100- <li>gif</li>
101- <li>webp</li>
102- <li>svg</li>
103- <li>ico</li>
104- <li>pdf</li>
105- <li>json</li>
106- <li>txt</li>
107- <li>otf</li>
108- <li>ttf</li>
109- <li>woff</li>
110- <li>woff2</li>
111- <li>md</li>
112- <li>rss</li>
113- <li>xml</li>
114- <li>atom</li>
115- <li>map</li>
116- <li>webmanifest</li>
117- </ul>
118- </section>
119-
120- <section id="redirects">
121- <h2 class="text-xl">
122- <a href="#redirects" rel="nofollow noopener">#</a>
123- User-defined Redirects
124- </h2>
125- <p>
126- We support custom redirects via a special file <code>_redirects</code>.
127- Read more about it at <a href="https://docs.netlify.com/routing/redirects">netflify</a>
128- </p>
129- </section>
130-
131- <section id="spa">
132- <h2 class="text-xl">
133- <a href="#spa" rel="nofollow noopener">#</a>
134- Single-Page Applications (SPAs)
135- </h2>
136- <p>
137- pgs supports SPAs! Upload a <code>_redirects</code> file your project:
138- </p>
139- <pre>/* /index.html 200</pre>
140-
141- <p>
142- Note that the Content Security Policy (CSP) for pages in the pgs.sh domain
143- prohibits javascript from connecting to other origins.
144- Pages served by custom domains do not have this restriction.
145- </p>
146-
147- </section>
148-
149- <section id="custom-domain">
150- <h2 class="text-xl">
151- <a href="#custom-domain" rel="nofollow noopener">#</a>
152- Setup a custom domain
153- </h2>
154- <p>
155- A blog can be accessed from a custom domain.
156- HTTPS will be automatically enabled and a certificate will be retrieved
157- from <a href="https://letsencrypt.org/">Let's Encrypt</a>. In order for this to work,
158- 2 DNS records need to be created:
159- </p>
160-
161- <p>CNAME for the domain to pgs (subdomains or DNS hosting with CNAME flattening) or A record</p>
162- <pre>CNAME subdomain.yourcustomdomain.com -> {{.Site.Domain}}</pre>
163- <p>Resulting in:</p>
164- <pre>subdomain.yourcustomdomain.com. 300 IN CNAME {{.Site.Domain}}.</pre>
165- <p>And a TXT record to tell Prose what project is hosted on that domain at the subdomain entry _pgs</p>
166- <pre>TXT _pgs.subdomain.yourcustomdomain.com -> username-myproject</pre>
167- <p>E.g. for user 'erock' and project 'kittens' the result would be:</p>
168- <p>Resulting in:</p>
169- <pre>_pgs.subdomain.yourcustomdomain.com. 300 IN TXT "erock-kittens"</pre>
170- </section>
171-</main>
172-{{template "marketing-footer" .}}
173-{{end}}
1@@ -2,11 +2,5 @@
2 <footer>
3 <hr />
4 <p class="font-italic">Built and maintained by <a href="https://pico.sh">pico.sh</a>.</p>
5- <div>
6- <a href="/">home</a> |
7- <a href="/ops.html">ops</a> |
8- <a href="/help.html">help</a> |
9- <a href="https://github.com/picosh/pico">source</a>
10- </div>
11 </footer>
12 {{end}}
+6,
-126
1@@ -28,134 +28,14 @@
2
3 {{define "body"}}
4 <header class="text-center">
5- <h1 class="text-2xl font-bold">{{.Site.Domain}} [closed beta]</h1>
6+ <h1 class="text-2xl font-bold">{{.Site.Domain}}</h1>
7 <p class="text-lg">{{.Site.Description}}</p>
8- <hr />
9+ <div>
10+ <a href="https://pico.sh/pgs" class="btn-link mt inline-block">
11+ LEARN MORE
12+ </a>
13+ </div>
14 </header>
15
16-<main>
17- <section>
18- <h2 class="text-lg font-bold">Closed beta</h2>
19- <p>Anyone can get an invite!</p>
20- <p>
21- The <strong>only</strong> requirement is to stay in our IRC channel
22- and be willing to provide feedback on how we can improve the service.
23- </p>
24- <p>Join our IRC channel #pico.sh @ libera.chat and ask for an invite.</p>
25- </section>
26-
27- <section>
28- <h2 class="text-lg font-bold">Examples</h2>
29- <ul>
30- <li>The site you are reading right now</li>
31- <li><a href="https://git.erock.io">git web viewer</a></li>
32- <li><a href="https://neovimcraft.com">neovimcraft</a></li>
33- </ul>
34- </section>
35-
36- <section>
37- <h2 class="text-lg font-bold">Features</h2>
38- <ul>
39- <li>Terminal workflow</li>
40- <li>No client-side installation required to fully manage static sites</li>
41- <li>Distinct static sites as <code>projects</code></li>
42- <li>Unlimited projects created on-the-fly (no need to create a project first)</li>
43- <li>Deploy via <code>scp -r ./public/* erock@{{.Site.Domain}}:/myproject</code></li>
44- <li>Promotion/rollback support (via symbolic linking from one project to another)</li>
45- <li>Managed HTTPS for all projects (e.g. https://erock-myproject.{{.Site.Domain}})</li>
46- <li><a href="/help#custom-domain">Custom domains</a> for projects (managed simply by `TXT` records)</li>
47- <li><a href="/help#redirects">User-defined redirects</a></li>
48- <li><a href="/help#spa">SPA support</a></li>
49- <li>1GB max storage</li>
50- <li>50MB max file size</li>
51- <li>All assets are public-only</li>
52- <li>Only web assets are supported</li>
53- </ul>
54- </section>
55-
56- <section>
57- <h2 class="text-lg font-bold">Create your account with Public-Key Cryptography</h2>
58- <p>We don't want your email address.</p>
59- <p>To get started, simply ssh into our content management system:</p>
60- <pre>ssh new@{{.Site.Domain}}</pre>
61- <div class="text-sm font-italic note">
62- note: <code>new</code> is a special username that will always send you to account
63- creation, even with multiple accounts associated with your key-pair.
64- </div>
65- <div class="text-sm font-italic note">
66- note: getting permission denied? <a href="/help.html#permission-denied">read this</a>
67- </div>
68- <p>
69- After that, just set a username and you're ready to start writing! When you SSH
70- again, use your username that you set in the CMS.
71- </p>
72- </section>
73-
74- <section>
75- <h2 class="text-lg font-bold">Publish your site with one command</h2>
76- <p>
77- When your site is ready to be published, copy the files to our server with a familiar
78- command:
79- </p>
80- <pre>scp -r * {{.Site.Domain}}:/myproject</pre>
81- <p>
82- That's it! There's no need to formally create a project, we
83- create them on-the-fly. Further, we provide TLS for every project
84- automatically. In this case the url for the project above would
85- look something like <code>https://{username}-myproject.{{.Site.Domain}}</code>.
86- </p>
87- </section>
88-
89- <section>
90- <h2 class="text-lg font-bold">Manage your projects with a remote CLI</h2>
91- <p>
92- Our management system is done via ssh commands. Type the following command to learn more:
93- </p>
94- <pre>ssh {{.Site.Domain}} help</pre>
95- </section>
96-
97- <section>
98- <h2 class="text-lg font-bold">Project promotion and rollbacks</h2>
99- <p>
100- Additionally you can setup a pipeline for promotion and rollbacks,
101- which will instantly update your project.
102- </p>
103- <pre>ssh {{.Site.Domain}} link project-prod project-d0131d4</pre>
104- <p>
105- A common way to perform promotions within {{.Site.Domain}} is to setup CI/CD so every
106- push to <code>main</code> would trigger a build and create a new project
107- based on the git commit hash (e.g. <code>project-d0131d4</code>).
108- </p>
109- <p>
110- This command will create a symbolic link from <code>project-prod</code>
111- to <code>project-d0131d4</code>. Want to rollback a release?
112- Just change the link for <code>project-prod</code> to a previous project.
113- </p>
114- <p>
115- We also built a <a href="https://github.com/picosh/pgs-action">github action</a>
116- that handles all the logic for uploading to {{.Site.Domain}}.
117- <a href="https://erock-git-neovimcraft.pgs.sh/tree/main/item/.github/workflows/deploy.yml.html#27">Here's an example of it in action</a>.
118- </p>
119- </section>
120-
121- <section>
122- <h2 class="text-lg font-bold">Philosophy</h2>
123- <p>
124- Creating a static website should be as simple as copying files from a local folder to a server.
125- We also believe in creating limited but feature-rich products without the user needing to install
126- anything on their computer.
127- </p>
128- <p>Read more about team pico's philosophy <a href="https://pico.sh">here</a>.</p>
129- </section>
130-
131- <section>
132- <h2 class="text-lg font-bold">Roadmap</h2>
133- <ol>
134- <li>Should we charge customers for more storage?</li>
135- <li>Github integration (e.g. post comment on PR with preview site)</li>
136- </ol>
137- </section>
138-</main>
139-
140 {{template "marketing-footer" .}}
141 {{end}}
+0,
-146
1@@ -1,146 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}operations -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} operations" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Operations</h1>
15- <ul>
16- <li><a href="/privacy.html">privacy</a></li>
17- </ul>
18-</header>
19-<main>
20- <section>
21- <h2 class="text-xl">Purpose</h2>
22- <p>
23- {{.Site.Domain}} exists to allow people to create and share their thoughts
24- without the need to set up their own server or be part of a platform
25- that shows ads or tracks its users.
26- </p>
27- </section>
28- <section>
29- <h2 class="text-xl">Ethics</h2>
30- <p>We are committed to:</p>
31- <ul>
32- <li>No browser-based tracking of visitor behavior.</li>
33- <li>No attempt to identify users.</li>
34- <li>Never sell any user or visitor data.</li>
35- <li>No ads — ever.</li>
36- </ul>
37- </section>
38- <section>
39- <h2 class="text-xl">Code of Content Publication</h2>
40- <p>
41- Content in {{.Site.Domain}} blogs is unfiltered and unmonitored. Users are free to publish any
42- combination of words and pixels except for: content of animosity or disparagement of an
43- individual or a group on account of a group characteristic such as race, color, national
44- origin, sex, disability, religion, or sexual orientation, which will be taken down
45- immediately.
46- </p>
47- <p>
48- If one notices something along those lines in a blog please let us know at
49- <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>.
50- </p>
51- </section>
52- <section>
53- <h2 class="text-xl">Liability</h2>
54- <p>
55- The user expressly understands and agrees that Eric Bower and Antonio Mika, the operator of this website
56- shall not be liable, in law or in equity, to them or to any third party for any direct,
57- indirect, incidental, lost profits, special, consequential, punitive or exemplary damages.
58- </p>
59- </section>
60- <section>
61- <h2 class="text-xl">Analytics</h2>
62- <p>
63- We are committed to zero browser-based tracking or trying to identify visitors. This
64- means we do not try to understand the user based on cookies or IP address. We do not
65- store personally identifiable information.
66- </p>
67- <p>
68- However, in order to provide a better service, we do have some analytics on posts.
69- List of metrics we track for posts:
70- </p>
71- <ul>
72- <li>anonymous view counts</li>
73- </ul>
74- <p>
75- We might also inspect the headers of HTTP requests to determine some tertiary information
76- about the request. For example we might inspect the <code>User-Agent</code> or
77- <code>Referer</code> to filter out requests from bots.
78- </p>
79- </section>
80- <section>
81- <h2 class="text-xl">Account Terms</h2>
82- <p>
83- <ul>
84- <li>
85- The user is responsible for all content posted and all actions performed with
86- their account.
87- </li>
88- <li>
89- We reserve the right to disable or delete a user's account for any reason at
90- any time. We have this clause because, statistically speaking, there will be
91- people trying to do something nefarious.
92- </li>
93- </ul>
94- </p>
95- </section>
96- <section>
97- <h2 class="text-xl">Service Availability</h2>
98- <p>
99- We provide the {{.Site.Domain}} service on an "as is" and "as available" basis. We do not offer
100- service-level agreements but do take uptime seriously.
101- </p>
102- </section>
103- <section>
104- <h2 class="text-xl">Contact and Support</h2>
105- <p>
106- Email us at <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>
107- with any questions.
108- </p>
109- </section>
110- <section>
111- <h2 class="text-xl">Acknowledgments</h2>
112- <p>
113- {{.Site.Domain}} was inspired by <a href="https://mataroa.blog">Mataroa Blog</a>
114- and <a href="https://bearblog.dev/">Bear Blog</a>.
115- </p>
116- <p>
117- {{.Site.Domain}} is built with many open source technologies.
118- </p>
119- <p>
120- In particular we would like to thank:
121- </p>
122- <ul>
123- <li>
124- <span>The </span>
125- <a href="https://charm.sh">charm.sh</a>
126- <span> community</span>
127- </li>
128- <li>
129- <span>The </span>
130- <a href="https://go.dev">golang</a>
131- <span> community</span>
132- </li>
133- <li>
134- <span>The </span>
135- <a href="https://www.postgresql.org/">postgresql</a>
136- <span> community</span>
137- </li>
138- <li>
139- <span>The </span>
140- <a href="https://github.com/caddyserver/caddy">caddy</a>
141- <span> community</span>
142- </li>
143- </ul>
144- </section>
145-</main>
146-{{template "marketing-footer" .}}
147-{{end}}
+0,
-54
1@@ -1,54 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}privacy -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} privacy policy" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Privacy</h1>
15- <p>Details on our privacy and security approach.</p>
16-</header>
17-<main>
18- <section>
19- <h2 class="text-xl">Account Data</h2>
20- <p>
21- In order to have a functional account at {{.Site.Domain}}, we need to store
22- your public key. That is the only piece of information we record for a user.
23- </p>
24- <p>
25- Because we use public-key cryptography, our security posture is a battle-tested
26- and proven technique for authentication.
27- </p>
28- </section>
29-
30- <section>
31- <h2 class="text-xl">Third parties</h2>
32- <p>
33- We have a strong commitment to never share any user data with any third-parties.
34- </p>
35- </section>
36-
37- <section>
38- <h2 class="text-xl">Service Providers</h2>
39- <ul>
40- <li>
41- <span>We host our server on </span>
42- <a href="https://www.oracle.com/cloud/">oracle cloud</a>
43- </li>
44- </ul>
45- </section>
46-
47- <section>
48- <h2 class="text-xl">Cookies</h2>
49- <p>
50- We do not use any cookies, not even account authentication.
51- </p>
52- </section>
53-</main>
54-{{template "marketing-footer" .}}
55-{{end}}
+23,
-0
1@@ -17,6 +17,7 @@
2
3 @media (prefers-color-scheme: light) {
4 :root {
5+ --main-hue: 250;
6 --white: #6a737d;
7 --code: #fff8d3;
8 --code-border: #f0d547;
9@@ -34,6 +35,7 @@
10
11 @media (prefers-color-scheme: dark) {
12 :root {
13+ --main-hue: 250;
14 --white: #f2f2f2;
15 --code: #414558;
16 --code-border: #252525;
17@@ -271,6 +273,23 @@ figure {
18 margin: 0;
19 }
20
21+.btn-link {
22+ border: 3px solid hsl(var(--main-hue), 92%, 66%);
23+ background-color: hsl(var(--main-hue), 92%, 66%);
24+ padding: 0.5rem 1rem;
25+ border-radius: 0.25rem;
26+ box-shadow: 0px 1px 2px 0px black;
27+ color: var(--white);
28+ text-decoration: none;
29+ font-weight: bold;
30+}
31+
32+.btn-link:visited,
33+.btn-link:visited:hover,
34+.btn-link:hover {
35+ color: var(--white);
36+}
37+
38 .post-date {
39 width: 130px;
40 }
41@@ -319,6 +338,10 @@ figure {
42 display: inline;
43 }
44
45+.inline-block {
46+ display: inline-block;
47+}
48+
49 .flex {
50 display: flex;
51 }
+0,
-66
1@@ -1,66 +0,0 @@
2-package pgs
3-
4-import (
5- "bytes"
6- "os"
7- "path/filepath"
8-
9- "github.com/picosh/pico/shared"
10-)
11-
12-type PageData struct {
13- Site shared.SitePageData
14-}
15-
16-type Page struct {
17- src string
18- dest string
19- cfg *shared.ConfigSite
20-}
21-
22-func genPage(page *Page) error {
23- ts, err := shared.RenderTemplate(page.cfg, []string{page.cfg.StaticPath(page.src)})
24-
25- if err != nil {
26- page.cfg.Logger.Error(err)
27- return err
28- }
29-
30- data := PageData{
31- Site: *page.cfg.GetSiteData(),
32- }
33- buf := new(bytes.Buffer)
34- err = ts.Execute(buf, data)
35- if err != nil {
36- page.cfg.Logger.Error(err)
37- return err
38- }
39-
40- err = os.WriteFile(page.dest, buf.Bytes(), 0644)
41- if err != nil {
42- page.cfg.Logger.Fatal(err)
43- }
44-
45- return nil
46-}
47-
48-func GenStaticSite(dir string, cfg *shared.ConfigSite) error {
49- pages := [][2]string{
50- {"html/marketing.page.tmpl", "index.html"},
51- {"html/ops.page.tmpl", "ops.html"},
52- {"html/privacy.page.tmpl", "privacy.html"},
53- {"html/help.page.tmpl", "help.html"},
54- }
55- for _, page := range pages {
56- err := genPage(&Page{
57- src: page[0],
58- dest: filepath.Join(dir, page[1]),
59- cfg: cfg,
60- })
61- if err != nil {
62- return err
63- }
64- }
65-
66- return nil
67-}
+1,
-40
1@@ -468,40 +468,6 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
2 }
3 }
4
5-func transparencyHandler(w http.ResponseWriter, r *http.Request) {
6- dbpool := shared.GetDB(r)
7- logger := shared.GetLogger(r)
8- cfg := shared.GetCfg(r)
9-
10- analytics, err := dbpool.FindSiteAnalytics(cfg.Space)
11- if err != nil {
12- logger.Error(err)
13- http.Error(w, err.Error(), http.StatusInternalServerError)
14- return
15- }
16-
17- ts, err := template.ParseFiles(
18- cfg.StaticPath("html/transparency.page.tmpl"),
19- cfg.StaticPath("html/footer.partial.tmpl"),
20- cfg.StaticPath("html/marketing-footer.partial.tmpl"),
21- cfg.StaticPath("html/base.layout.tmpl"),
22- )
23-
24- if err != nil {
25- http.Error(w, err.Error(), http.StatusInternalServerError)
26- }
27-
28- data := TransparencyPageData{
29- Site: *cfg.GetSiteData(),
30- Analytics: analytics,
31- }
32- err = ts.Execute(w, data)
33- if err != nil {
34- logger.Error(err)
35- http.Error(w, err.Error(), http.StatusInternalServerError)
36- }
37-}
38-
39 func readHandler(w http.ResponseWriter, r *http.Request) {
40 dbpool := shared.GetDB(r)
41 logger := shared.GetLogger(r)
42@@ -832,12 +798,7 @@ func createStaticRoutes() []shared.Route {
43
44 func createMainRoutes(staticRoutes []shared.Route) []shared.Route {
45 routes := []shared.Route{
46- shared.NewRoute("GET", "/", shared.CreatePageHandler("html/marketing.page.tmpl")),
47- shared.NewRoute("GET", "/spec", shared.CreatePageHandler("html/spec.page.tmpl")),
48- shared.NewRoute("GET", "/ops", shared.CreatePageHandler("html/ops.page.tmpl")),
49- shared.NewRoute("GET", "/privacy", shared.CreatePageHandler("html/privacy.page.tmpl")),
50- shared.NewRoute("GET", "/help", shared.CreatePageHandler("html/help.page.tmpl")),
51- shared.NewRoute("GET", "/transparency", transparencyHandler),
52+ shared.NewRoute("GET", "/", readHandler),
53 shared.NewRoute("GET", "/read", readHandler),
54 shared.NewRoute("GET", "/check", shared.CheckHandler),
55 }
+0,
-564
1@@ -1,564 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}help -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="questions and answers" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Need help?</h1>
15- <p>Here are some common questions on using this platform that we would like to answer.</p>
16-</header>
17-<main>
18- <section id="permission-denied">
19- <h2 class="text-xl">
20- <a href="#permission-denied" rel="nofollow noopener">#</a>
21- I get a permission denied when trying to SSH
22- </h2>
23- <p>
24- Unfortunately SHA-2 RSA keys are <strong>not</strong> currently supported.
25- </p>
26- <p>
27- Unfortunately, due to a shortcoming in Go’s x/crypto/ssh package, we do
28- not currently support access via new SSH RSA keys: only the old SHA-1 ones will work.
29- Until we sort this out you’ll either need an SHA-1 RSA key or a key with another
30- algorithm, e.g. Ed25519. Not sure what type of keys you have? You can check with the
31- following:
32- </p>
33- <pre>$ find ~/.ssh/id_*.pub -exec ssh-keygen -l -f {} \;</pre>
34- <p>If you’re curious about the inner workings of this problem have a look at:</p>
35- <ul>
36- <li><a href="https://github.com/golang/go/issues/37278">golang/go#37278</a></li>
37- <li><a href="https://go-review.googlesource.com/c/crypto/+/220037">go-review</a></li>
38- <li><a href="https://github.com/golang/crypto/pull/197">golang/crypto#197</a></li>
39- </ul>
40- </section>
41-
42- <section id="blog-ssh-key">
43- <h2 class="text-xl">
44- <a href="#blog-ssh-key" rel="nofollow noopener">#</a>
45- Generating a new SSH key
46- </h2>
47- <p>
48- <a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">Github reference</a>
49- </p>
50- <pre>ssh-keygen -t ed25519 -C "your_email@example.com"</pre>
51- <ol>
52- <li>When you're prompted to "Enter a file in which to save the key," press Enter. This accepts the default file location.</li>
53- <li>At the prompt, type a secure passphrase.</li>
54- </ol>
55- </section>
56-
57- <section id="blog-structure">
58- <h2 class="text-xl">
59- <a href="#blog-structure" rel="nofollow noopener">#</a>
60- What should my blog folder look like?
61- </h2>
62- <p>
63- Currently {{.Site.Domain}} only supports a flat folder structure. Therefore,
64- <code>scp -r</code> is not permitted. We also only allow <code>.md</code> files to be
65- uploaded.
66- </p>
67- <p>
68- <a href="https://github.com/neurosnap/prose-blog">Here is the source to my blog on this platform</a>
69- </p>
70- <p>
71- Below is an example of what your blog folder should look like:
72- </p>
73- <pre>blog/
74-first-post.md
75-second-post.md
76-third-post.md</pre>
77- </p>
78- <p>
79- Underscores and hyphens are permitted and will be automatically removed from the title of the post.
80- </p>
81- </section>
82-
83- <section id="post-metadata">
84- <h2 class="text-xl">
85- <a href="#post-metadata" rel="nofollow noopener">#</a>
86- How do I update metadata like publish date and title?
87- </h2>
88- <p>
89- We support adding frontmatter to the top of your markdown posts. A frontmatter looks like the following:
90- <pre>---
91-title: some title!
92-description: this is a great description
93-date: 2022-06-28
94----</pre>
95- </p>
96- <p>
97- List of available variables can be found <a href="#hugo-support">here</a>.
98- </p>
99- <p>
100- Common variables:
101- <ul>
102- <li>title (custom title not dependent on filename)</li>
103- <li>description (what is the purpose of this post? It's also added to meta tag)</li>
104- <li>date (suggested format YYYY-MM-DD, if set in future post wil be unlisted)</li>
105- <li>tags (<code>[feature, announcement]</code>)</li>
106- <li>image (og image)</li>
107- <li>card (og image twitter card: summary, summary_large_image, etc.)</li>
108- </ul>
109- </p>
110- </section>
111-
112- <section id="post-footer">
113- <h2 class="text-xl">
114- <a href="#post-footer" rel="nofollow noopener">#</a>
115- How can I add a footer to all of my posts?
116- </h2>
117- <p>
118- We have a special file <code>_footer.md</code> that will be appended to every
119- single blog post.
120-
121- </p>
122- <p>
123- There is nothing that differentiates itself from the rest of the
124- post so it's up to you to style it. For convenience we added an <code>id</code> to the
125- containing element <code>post-footer</code>.
126- </p>
127- </section>
128-
129- <section id="post-update">
130- <h2 class="text-xl">
131- <a href="#post-update" rel="nofollow noopener">#</a>
132- How do I update a post?
133- </h2>
134- <p>
135- Updating a post requires that you update the source document and then run the <code>rsync</code>
136- command again. If the filename remains the same, then the post will be updated.
137- </p>
138- </section>
139-
140- <section id="post-delete">
141- <h2 class="text-xl">
142- <a href="#post-delete" rel="nofollow noopener">#</a>
143- How do I delete a post?
144- </h2>
145-
146- <h3 class="text-lg">
147- <a href="#post-delete-sftp" rel="nofollow noopener">#</a>
148- sftp
149- </h3>
150-
151- <p>There are three ways to delete posts: <code>sftp</code>, upload a 0-byte file, or use our CMS.</p>
152-
153- <pre>sftp {{.Site.Domain}}
154-sftp> rm delete.md</pre>
155-
156- <h3 class="text-lg">
157- <a href="#post-delete-0-byte-file" rel="nofollow noopener">#</a>
158- 0-byte file
159- </h3>
160-
161- <p>
162- Because <code>scp</code> does not natively support deleting files, we didn't want to bake
163- that behavior into my ssh server.
164- </p>
165-
166- <p>
167- However, if a user wants to delete a post they can delete the contents of the file and
168- then upload it to our server. If the file contains 0 bytes, we will remove the post.
169- For example, if you want to delete <code>delete.md</code> you could:
170- </p>
171-
172- <pre>
173-cp /dev/null delete.md
174-scp ./delete.md {{.Site.Domain}}:/</pre>
175-
176- <h3 class="text-lg">
177- <a href="#post-delete-cms" rel="nofollow noopener">#</a>
178- CMS
179- </h3>
180-
181- <p>
182- Alternatively, you can go to <code>ssh {{.Site.Domain}}</code> and select "Manage posts."
183- Then you can highlight the post you want to delete and then press "X." It will ask for
184- confirmation before actually removing the post.
185- </p>
186- </section>
187-
188- <section id="blog-upload-single-file">
189- <h2 class="text-xl">
190- <a href="#blog-upload-single-file" rel="nofollow noopener">#</a>
191- When I want to publish a new post, do I have to upload all posts everytime?
192- </h2>
193- <p>
194- Nope! Just <code>rsync</code> the file you want to publish. For example, if you created
195- a new post called <code>taco-tuesday.md</code> then you would publish it like this:
196- </p>
197- <pre>rsync ./taco-tuesday.md {{.Site.Domain}}:</pre>
198- </section>
199-
200- <section id="blog-readme">
201- <h2 class="text-xl">
202- <a href="#blog-readme" rel="nofollow noopener">#</a>
203- How can I customize my blog page?
204- </h2>
205- <p>
206- There's a special file you can upload `_readme.md` which will allow
207- users to add a bio and links to their blog landing page.
208- <pre>---
209-title: some title!
210-description: this is a great description
211-nav:
212- - google: https://google.com
213- - site: https://some.site
214----
215-
216-Here is a quick intro to my personal blog!
217-This will show up on the blog landing page.
218-</pre>
219- </p>
220- <p>
221- List of available variables:
222- <ul>
223- <li>title (name of the blog, default: "X's blog")</li>
224- <li>description (description of blog)</li>
225- <li>nav (key=value pair that corresponds to text=href in html)</li>
226- <li>image (og image)</li>
227- <li>card (og image twitter card: summary, summary_large_image, etc.)</li>
228- <li>favicon</li>
229- <li>aliases (list of alternate routes e.g. <code>2023/03/10/my-post</code>)</li>
230- </ul>
231- </p>
232- </section>
233-
234- <section id="blog-style">
235- <h2 class="text-xl">
236- <a href="#blog-style" rel="nofollow noopener">#</a>
237- How can I change the theme of my blog?
238- </h2>
239- <p>
240- There's a special file you can upload `_styles.css` which will allow
241- users to add a CSS file to their page. It will be the final CSS file
242- loaded on the page so it will overwrite whatever styles have previously
243- been added. We've also added a couple of convenience id's attached to the
244- body element for the blog and post pages.
245- </p>
246- <pre>/* _styles.css */
247-#post {
248- color: green;
249-}
250-
251-#blog {
252- color: tomato;
253-}</pre>
254- <p>Then just upload the file:</p>
255- <pre>sync _styles.css <username>@prose.sh:/</pre>
256- </section>
257-
258- <section id="blog-layout">
259- <h2 class="text-xl">
260- <a href="#blog-layout" rel="nofollow noopener">#</a>
261- How can I change the layout of my blog?
262- </h2>
263- <p>
264- Inside the <code>_header.txt</code> metadata file, there's a variable <code>layout</code>
265- option that will change the layout of your blog index page.
266- </p>
267- <p>Currently supported options</p>
268- <ul>
269- <li>default</li>
270- <li>aside</li>
271- </ul>
272- </section>
273-
274- <section id="images">
275- <h2 class="text-xl">
276- <a href="#images" rel="nofollow noopener">#</a>
277- How can I upload images?
278- </h2>
279- <p>
280- We allow prose to upload images to imgs.
281- For example, if you want to upload images for prose.sh all you have to do is include
282- your images in the <code>rsync</code> or <code>scp</code> command.
283- </p>
284- <pre>rsync profile.jpg user@prose.sh:/</pre>
285- <p>Then when you want to reference the file, you can reference it like so:</p>
286- <pre>![profile pic](/profile.jpg)</pre>
287- </section>
288-
289- <section id="file-types">
290- <h2 class="text-xl">
291- <a href="#file-types" rel="nofollow noopener">#</a>
292- What file types are supported?
293- </h2>
294- <p>
295- <a href="https://imgs.sh/help">see imgs help page</a>
296- </p>
297- </section>
298-
299- <section id="blog-url">
300- <h2 class="text-xl">
301- <a href="#blog-url" rel="nofollow noopener">#</a>
302- What is my blog URL?
303- </h2>
304- <pre>https://{username}.{{.Site.Domain}}</pre>
305- </section>
306-
307- <section id="continuous-deployment">
308- <h2 class="text-xl">
309- <a href="#continuous-deployment" rel="nofollow noopener">#</a>
310- How can I automatically publish my post?
311- </h2>
312- <p>
313- There is a github action that we built to make it easy to publish your blog automatically.
314- </p>
315- <ul>
316- <li>
317- <a href="https://github.com/picosh/scp-publish-action">github action repo</a>
318- </li>
319- <li>
320- <a href="https://github.com/neurosnap/lists-official-blog/blob/main/.github/workflows/publish.yml">example workflow</a>
321- </li>
322- </ul>
323- <p>
324- A user also created a systemd task to automatically publish new posts. <a href="https://github.com/neurosnap/lists.sh/discussions/24">Check out this github discussion for more details.</a>
325- </p>
326- </section>
327-
328- <section id="multiple-accounts">
329- <h2 class="text-xl">
330- <a href="#multiple-accounts" rel="nofollow noopener">#</a>
331- Can I create multiple accounts?
332- </h2>
333- <p>
334- Yes! You can either a) create a new keypair and use that for authentication
335- or b) use the same keypair and ssh into our CMS using our special username
336- <code>ssh new@{{.Site.Domain}}</code>.
337- </p>
338- <p>
339- Please note that if you use the same keypair for multiple accounts, you will need to
340- always specify the user when logging into our CMS.
341- </p>
342- </section>
343-
344- <section id="custom-domain">
345- <h2 class="text-xl">
346- <a href="#custom-domain" rel="nofollow noopener">#</a>
347- Setup a custom domain
348- </h2>
349- <p>
350- A blog can be accessed from a custom domain.
351- HTTPS will be automatically enabled and a certificate will be retrieved
352- from <a href="https://letsencrypt.org/">Let's Encrypt</a>. In order for this to work,
353- 2 DNS records need to be created:
354- </p>
355-
356- <p><code>CNAME</code> for the domain to prose (subdomains or DNS hosting with <code>CNAME</code> flattening) or <code>A</code> record</p>
357- <pre>CNAME subdomain.yourcustomdomain.com -> prose.sh</pre>
358- <p>Resulting in:</p>
359-
360- <pre>subdomain.yourcustomdomain.com. 300 IN CNAME prose.sh.</pre>
361-
362- <p>And a <code>TXT</code> record to tell Prose what blog is hosted on that domain at the subdomain entry <code>_prose</code></p>
363- <pre>TXT _prose.subdomain.yourcustomdomain.com -> yourproseusername</pre>
364- <p>Resulting in:</p>
365- <pre>_prose.subdomain.yourcustomdomain.com. 300 IN TXT "hey"</pre>
366-
367- <p>
368- Depending on your DNS, this could take some time to fully switch over. We have an endpoint
369- to check whether or not custom domains are setup:
370- <pre>curl -vvvv https://prose.sh/check?domain=<DOMAIN></pre>
371- </p>
372- </section>
373-
374- <section id="hugo-support">
375- <h2 class="text-xl">
376- <a href="#hugo-support" rel="nofollow noopener">#</a>
377- Hugo support
378- </h2>
379- <p>
380- We support a subset of pre-defined hugo variables in our frontmatter.
381- <a href="https://gohugo.io/content-management/front-matter/">Complete list at Hugo</a>
382- </p>
383- <table>
384- <th>
385- <tr>
386- <th align="left">Variable</th>
387- <th>Supported</th>
388- <th>Planned</th>
389- </tr>
390- </th>
391-
392- <tbody>
393- <tr>
394- <td>aliases</td>
395- <td>yes</td>
396- <td>-</td>
397- </tr>
398-
399- <tr>
400- <td>audio</td>
401- <td>no</td>
402- <td>no</td>
403- </tr>
404-
405- <tr>
406- <td>cascade</td>
407- <td>no</td>
408- <td>no</td>
409- </tr>
410-
411- <tr>
412- <td>date</td>
413- <td>yes</td>
414- <td>-</td>
415- </tr>
416-
417- <tr>
418- <td>description</td>
419- <td>yes</td>
420- <td>-</td>
421- </tr>
422-
423- <tr>
424- <td>draft</td>
425- <td>yes</td>
426- <td>-</td>
427- </tr>
428-
429- <tr>
430- <td>expiryDate</td>
431- <td>no</td>
432- <td>no</td>
433- </tr>
434-
435- <tr>
436- <td>headless</td>
437- <td>no</td>
438- <td>no</td>
439- </tr>
440-
441- <tr>
442- <td>images</td>
443- <td>no</td>
444- <td>no</td>
445- </tr>
446-
447- <tr>
448- <td>isCJKLanguage</td>
449- <td>no</td>
450- <td>no</td>
451- </tr>
452-
453- <tr>
454- <td>keywords</td>
455- <td>no</td>
456- <td>yes</td>
457- </tr>
458-
459- <tr>
460- <td>layout</td>
461- <td>no</td>
462- <td>maybe</td>
463- </tr>
464-
465- <tr>
466- <td>lastmod</td>
467- <td>no</td>
468- <td>maybe</td>
469- </tr>
470-
471- <tr>
472- <td>linkTitle</td>
473- <td>no</td>
474- <td>no</td>
475- </tr>
476-
477- <tr>
478- <td>markup</td>
479- <td>no</td>
480- <td>no</td>
481- </tr>
482-
483- <tr>
484- <td>outputs</td>
485- <td>no</td>
486- <td>no</td>
487- </tr>
488-
489- <tr>
490- <td>publishDate</td>
491- <td>no</td>
492- <td>no</td>
493- </tr>
494-
495- <tr>
496- <td>resources</td>
497- <td>no</td>
498- <td>no</td>
499- </tr>
500-
501- <tr>
502- <td>series</td>
503- <td>no</td>
504- <td>no</td>
505- </tr>
506-
507- <tr>
508- <td>slug</td>
509- <td>no</td>
510- <td>no</td>
511- </tr>
512-
513- <tr>
514- <td>summary</td>
515- <td>no</td>
516- <td>no</td>
517- </tr>
518-
519- <tr>
520- <td>tags</td>
521- <td>yes</td>
522- <td>-</td>
523- </tr>
524-
525- <tr>
526- <td>title</td>
527- <td>yes</td>
528- <td>-</td>
529- </tr>
530-
531- <tr>
532- <td>type</td>
533- <td>no</td>
534- <td>no</td>
535- </tr>
536-
537- <tr>
538- <td>url</td>
539- <td>no</td>
540- <td>no</td>
541- </tr>
542-
543- <tr>
544- <td>videos</td>
545- <td>no</td>
546- <td>no</td>
547- </tr>
548-
549- <tr>
550- <td>weight</td>
551- <td>no</td>
552- <td>no</td>
553- </tr>
554-
555- <tr>
556- <td>taxonomies</td>
557- <td>no</td>
558- <td>no</td>
559- </tr>
560- </tbody>
561- </table>
562- </section>
563-</main>
564-{{template "marketing-footer" .}}
565-{{end}}
1@@ -3,9 +3,7 @@
2 <hr />
3 <p class="font-italic">Built and maintained by <a href="https://pico.sh">pico.sh</a>.</p>
4 <div>
5- <a href="/">home</a> |
6- <a href="/ops">ops</a> |
7- <a href="/help">help</a> |
8+ <a href="/">discover</a> |
9 <a href="/rss">rss</a> |
10 <a href="https://github.com/picosh/pico">source</a>
11 </div>
+0,
-167
1@@ -1,167 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}{{.Site.Domain}} -- a blog platform for hackers{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="a blog platform for hackers" />
8-
9-<meta property="og:type" content="website">
10-<meta property="og:site_name" content="{{.Site.Domain}}">
11-<meta property="og:url" content="https://{{.Site.Domain}}">
12-<meta property="og:title" content="{{.Site.Domain}}">
13-<meta property="og:description" content="a blog platform for hackers">
14-
15-<meta name="twitter:card" content="summary" />
16-<meta property="twitter:url" content="https://{{.Site.Domain}}">
17-<meta property="twitter:title" content="{{.Site.Domain}}">
18-<meta property="twitter:description" content="a blog platform for hackers">
19-<meta name="twitter:image" content="https://{{.Site.Domain}}/card.png" />
20-<meta name="twitter:image:src" content="https://{{.Site.Domain}}/card.png" />
21-
22-<meta property="og:image:width" content="300" />
23-<meta property="og:image:height" content="300" />
24-<meta itemprop="image" content="https://{{.Site.Domain}}/card.png" />
25-<meta property="og:image" content="https://{{.Site.Domain}}/card.png" />
26-{{end}}
27-
28-{{define "attrs"}}{{end}}
29-
30-{{define "body"}}
31-<header class="text-center">
32- <h1 class="text-2xl font-bold">{{.Site.Domain}}</h1>
33- <p class="text-lg">a blog platform for hackers</p>
34- <p class="text-lg"><a href="/read">discover</a> some interesting posts</p>
35- <hr />
36-</header>
37-
38-<main>
39- <section>
40- <h2 class="text-lg font-bold">Create your account with Public-Key Cryptography</h2>
41- <p>We don't want your email address.</p>
42- <p>To get started, simply ssh into our content management system:</p>
43- <pre>ssh new@{{.Site.Domain}}</pre>
44- <div class="text-sm font-italic note">
45- note: <code>new</code> is a special username that will always send you to account
46- creation, even with multiple accounts associated with your key-pair.
47- </div>
48- <div class="text-sm font-italic note">
49- note: getting permission denied? <a href="/help#permission-denied">read this</a>
50- </div>
51- <p>
52- After that, just set a username and you're ready to start writing! When you SSH
53- again, use your username that you set in the CMS.
54- </p>
55- </section>
56-
57- <section>
58- <h2 class="text-lg font-bold">You control the source files</h2>
59- <p>Create posts using your favorite editor in plain text files.</p>
60- <code>~/blog/hello-world.md</code>
61- <pre># hello world!
62-
63-This is my first blog post.
64-
65-Check out some resources:
66-
67-- [pico.sh](https://pico.sh)
68-- [lists.sh](https://lists.sh)
69-- [antoniomika](https://antoniomika.me)
70-- [erock.io](https://erock.io)
71-
72-Cya!
73-</pre>
74- </section>
75-
76- <section>
77- <h2 class="text-lg font-bold">Upload images for your blog</h2>
78- <p>
79- We also support image uploading (jpg, png, gif, webp, svg). Simply upload your
80- images alongside your markdown files and then reference them from root `/`:
81- </p>
82- <pre>---
83-title: hello world!
84----
85-
86- ![profile pic](/profile.jpg)</pre>
87- <pre>rsync ~/blog/hello-world.md ~/blog/profile.jpg {{.Site.Domain}}:/</pre>
88- <p>Read more about image uploading at [imgs.sh](https://imgs.sh) and our [help page](/help)</p>
89- </section>
90-
91- <section>
92- <h2 class="text-lg font-bold">Publish your posts with one command</h2>
93- <p>
94- When your post is ready to be published, copy the file to our server with a familiar
95- command:
96- </p>
97- <pre>rsync ~/blog/*.md {{.Site.Domain}}:/</pre>
98- <p>We'll either create or update the posts for you.</p>
99- </section>
100-
101- <section>
102- <h2 class="text-lg font-bold">Terminal workflow without installation</h2>
103- <p>
104- Since we are leveraging tools you already have on your computer
105- (<code>ssh</code> and <code>rsync</code>), there is nothing to install.
106- </p>
107- <p>
108- This provides the convenience of a web app, but from inside your terminal!
109- </p>
110- </section>
111-
112- <section>
113- <h2 class="text-lg font-bold">Use <code>sftp</code> to manage your posts</h2>
114- <p>
115- FTP is back in style. List, upload, and delete posts via <code>sftp</code>:
116- </p>
117- <pre>sftp {{.Site.Domain}}
118-sftp> ls
119-hello-world.md
120-sftp> rm hello-world.md
121-sftp> put hello-world.md</pre>
122- </section>
123-
124- <section>
125- <h2 class="text-lg font-bold">Features</h2>
126- <ul>
127- <li>Github flavor markdown</li>
128- <li><a href="/help#custom-domain">Custom domains</a></li>
129- <li>Looks great on any device</li>
130- <li>Bring your own editor</li>
131- <li>You control the source files</li>
132- <li>Terminal workflow with no installation</li>
133- <li>Public-key based authentication</li>
134- <li>Use sftp to manage blog</li>
135- <li>No ads, zero browser-based tracking</li>
136- <li>No attempt to identify users</li>
137- <li>No platform lock-in</li>
138- <li>No javascript</li>
139- <li>Subscriptions via RSS</li>
140- <li>Minimalist design</li>
141- <li>100% open source</li>
142- </ul>
143- </section>
144-
145- <section>
146- <h2 class="text-lg font-bold">Philosophy</h2>
147- <p>
148- The goal of this blogging platform is to make it simple to use the tools you love to
149- write and publish your thoughts. There is no installation, signup is as easy as SSH'ing
150- into our CMS, and publishing content is as easy as copying files to our server.
151- </p>
152-
153- <p>
154- If you'd like to read more about our group, please read our profile at <a href="https://pico.sh">pico.sh</a>.
155- </p>
156- </section>
157-
158- <section>
159- <h2 class="text-lg font-bold">Roadmap</h2>
160- <ol>
161- <li>Writing competitions</li>
162- <li><a href="/help#hugo-support">Limited compatibility</a> with <a href="https://gohugo.io">hugo</a></li>
163- </ol>
164- </section>
165-</main>
166-
167-{{template "marketing-footer" .}}
168-{{end}}
+0,
-147
1@@ -1,147 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}operations -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} operations" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Operations</h1>
15- <ul>
16- <li><a href="/privacy">privacy</a></li>
17- <li><a href="/transparency">transparency</a></li>
18- </ul>
19-</header>
20-<main>
21- <section>
22- <h2 class="text-xl">Purpose</h2>
23- <p>
24- {{.Site.Domain}} exists to allow people to create and share their thoughts
25- without the need to set up their own server or be part of a platform
26- that shows ads or tracks its users.
27- </p>
28- </section>
29- <section>
30- <h2 class="text-xl">Ethics</h2>
31- <p>We are committed to:</p>
32- <ul>
33- <li>No browser-based tracking of visitor behavior.</li>
34- <li>No attempt to identify users.</li>
35- <li>Never sell any user or visitor data.</li>
36- <li>No ads — ever.</li>
37- </ul>
38- </section>
39- <section>
40- <h2 class="text-xl">Code of Content Publication</h2>
41- <p>
42- Content in {{.Site.Domain}} blogs is unfiltered and unmonitored. Users are free to publish any
43- combination of words and pixels except for: content of animosity or disparagement of an
44- individual or a group on account of a group characteristic such as race, color, national
45- origin, sex, disability, religion, or sexual orientation, which will be taken down
46- immediately.
47- </p>
48- <p>
49- If one notices something along those lines in a blog please let us know at
50- <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>.
51- </p>
52- </section>
53- <section>
54- <h2 class="text-xl">Liability</h2>
55- <p>
56- The user expressly understands and agrees that Eric Bower and Antonio Mika, the operator of this website
57- shall not be liable, in law or in equity, to them or to any third party for any direct,
58- indirect, incidental, lost profits, special, consequential, punitive or exemplary damages.
59- </p>
60- </section>
61- <section>
62- <h2 class="text-xl">Analytics</h2>
63- <p>
64- We are committed to zero browser-based tracking or trying to identify visitors. This
65- means we do not try to understand the user based on cookies or IP address. We do not
66- store personally identifiable information.
67- </p>
68- <p>
69- However, in order to provide a better service, we do have some analytics on posts.
70- List of metrics we track for posts:
71- </p>
72- <ul>
73- <li>anonymous view counts</li>
74- </ul>
75- <p>
76- We might also inspect the headers of HTTP requests to determine some tertiary information
77- about the request. For example we might inspect the <code>User-Agent</code> or
78- <code>Referer</code> to filter out requests from bots.
79- </p>
80- </section>
81- <section>
82- <h2 class="text-xl">Account Terms</h2>
83- <p>
84- <ul>
85- <li>
86- The user is responsible for all content posted and all actions performed with
87- their account.
88- </li>
89- <li>
90- We reserve the right to disable or delete a user's account for any reason at
91- any time. We have this clause because, statistically speaking, there will be
92- people trying to do something nefarious.
93- </li>
94- </ul>
95- </p>
96- </section>
97- <section>
98- <h2 class="text-xl">Service Availability</h2>
99- <p>
100- We provide the {{.Site.Domain}} service on an "as is" and "as available" basis. We do not offer
101- service-level agreements but do take uptime seriously.
102- </p>
103- </section>
104- <section>
105- <h2 class="text-xl">Contact and Support</h2>
106- <p>
107- Email us at <a href="mailto:{{.Site.Email}}">{{.Site.Email}}</a>
108- with any questions.
109- </p>
110- </section>
111- <section>
112- <h2 class="text-xl">Acknowledgments</h2>
113- <p>
114- {{.Site.Domain}} was inspired by <a href="https://mataroa.blog">Mataroa Blog</a>
115- and <a href="https://bearblog.dev/">Bear Blog</a>.
116- </p>
117- <p>
118- {{.Site.Domain}} is built with many open source technologies.
119- </p>
120- <p>
121- In particular we would like to thank:
122- </p>
123- <ul>
124- <li>
125- <span>The </span>
126- <a href="https://charm.sh">charm.sh</a>
127- <span> community</span>
128- </li>
129- <li>
130- <span>The </span>
131- <a href="https://go.dev">golang</a>
132- <span> community</span>
133- </li>
134- <li>
135- <span>The </span>
136- <a href="https://www.postgresql.org/">postgresql</a>
137- <span> community</span>
138- </li>
139- <li>
140- <span>The </span>
141- <a href="https://github.com/caddyserver/caddy">caddy</a>
142- <span> community</span>
143- </li>
144- </ul>
145- </section>
146-</main>
147-{{template "marketing-footer" .}}
148-{{end}}
+0,
-54
1@@ -1,54 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}privacy -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="{{.Site.Domain}} privacy policy" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Privacy</h1>
15- <p>Details on our privacy and security approach.</p>
16-</header>
17-<main>
18- <section>
19- <h2 class="text-xl">Account Data</h2>
20- <p>
21- In order to have a functional account at {{.Site.Domain}}, we need to store
22- your public key. That is the only piece of information we record for a user.
23- </p>
24- <p>
25- Because we use public-key cryptography, our security posture is a battle-tested
26- and proven technique for authentication.
27- </p>
28- </section>
29-
30- <section>
31- <h2 class="text-xl">Third parties</h2>
32- <p>
33- We have a strong commitment to never share any user data with any third-parties.
34- </p>
35- </section>
36-
37- <section>
38- <h2 class="text-xl">Service Providers</h2>
39- <ul>
40- <li>
41- <span>We host our server on </span>
42- <a href="https://www.oracle.com/cloud/">oracle cloud</a>
43- </li>
44- </ul>
45- </section>
46-
47- <section>
48- <h2 class="text-xl">Cookies</h2>
49- <p>
50- We do not use any cookies, not even account authentication.
51- </p>
52- </section>
53-</main>
54-{{template "marketing-footer" .}}
55-{{end}}
+5,
-3
1@@ -11,9 +11,11 @@
2
3 {{define "body"}}
4 <header class="text-center">
5- <h1 class="text-2xl font-bold">read</h1>
6- <p class="text-lg">recent posts</p>
7- <p class="text-lg"><a href="/rss">rss</a></p>
8+ <h1 class="text-2xl font-bold">prose.sh</h1>
9+ <p class="text-lg">A blog platform for hackers</p>
10+ <div>
11+ <a href="https://pico.sh/getting-started" class="btn-link mt inline-block">GET STARTED</a>
12+ </div>
13 <hr />
14 </header>
15 <main>
+0,
-59
1@@ -1,59 +0,0 @@
2-{{template "base" .}}
3-
4-{{define "title"}}transparency -- {{.Site.Domain}}{{end}}
5-
6-{{define "meta"}}
7-<meta name="description" content="full transparency of analytics and cost at {{.Site.Domain}}" />
8-{{end}}
9-
10-{{define "attrs"}}{{end}}
11-
12-{{define "body"}}
13-<header>
14- <h1 class="text-2xl">Transparency</h1>
15- <hr />
16-</header>
17-<main>
18- <section>
19- <h2 class="text-xl">Analytics</h2>
20- <p>
21- Here are some interesting stats on usage.
22- </p>
23-
24- <article>
25- <h2 class="text-lg">Total users</h2>
26- <div>{{.Analytics.TotalUsers}}</div>
27- </article>
28-
29- <article>
30- <h2 class="text-lg">New users in the last month</h2>
31- <div>{{.Analytics.UsersLastMonth}}</div>
32- </article>
33-
34- <article>
35- <h2 class="text-lg">Total posts</h2>
36- <div>{{.Analytics.TotalPosts}}</div>
37- </article>
38-
39- <article>
40- <h2 class="text-lg">New posts in the last month</h2>
41- <div>{{.Analytics.PostsLastMonth}}</div>
42- </article>
43-
44- <article>
45- <h2 class="text-lg">Users with at least one post</h2>
46- <div>{{.Analytics.UsersWithPost}}</div>
47- </article>
48- </section>
49-
50- <section>
51- <h2 class="text-xl">Service maintenance costs</h2>
52- <ul>
53- <li>Domain name $3.25/mo</li>
54- <li>Server $0.00/mo</li>
55- <li>Programmer $0.00/mo</li>
56- </ul>
57- </section>
58-</main>
59-{{template "marketing-footer" .}}
60-{{end}}
+23,
-0
1@@ -17,6 +17,7 @@
2
3 @media (prefers-color-scheme: light) {
4 :root {
5+ --main-hue: 250;
6 --white: #6a737d;
7 --code: #fff8d3;
8 --code-border: #f0d547;
9@@ -34,6 +35,7 @@
10
11 @media (prefers-color-scheme: dark) {
12 :root {
13+ --main-hue: 250;
14 --white: #f2f2f2;
15 --code: #414558;
16 --code-border: #252525;
17@@ -271,6 +273,23 @@ figure {
18 margin: 0;
19 }
20
21+.btn-link {
22+ border: 3px solid hsl(var(--main-hue), 92%, 66%);
23+ background-color: hsl(var(--main-hue), 92%, 66%);
24+ padding: 0.5rem 1rem;
25+ border-radius: 0.25rem;
26+ box-shadow: 0px 1px 2px 0px black;
27+ color: var(--white);
28+ text-decoration: none;
29+ font-weight: bold;
30+}
31+
32+.btn-link:visited,
33+.btn-link:visited:hover,
34+.btn-link:hover {
35+ color: var(--white);
36+}
37+
38 .post-date {
39 width: 130px;
40 }
41@@ -319,6 +338,10 @@ figure {
42 display: inline;
43 }
44
45+.inline-block {
46+ display: inline-block;
47+}
48+
49 .flex {
50 display: flex;
51 }
1@@ -142,7 +142,7 @@ func GetCustomDomain(logger *zap.SugaredLogger, host string, space string) strin
2 txt := fmt.Sprintf("_%s.%s", space, host)
3 records, err := net.LookupTXT(txt)
4 if err != nil {
5- logger.Error(err)
6+ logger.Info(err)
7 return ""
8 }
9
M
smol.css
+23,
-0
1@@ -17,6 +17,7 @@
2
3 @media (prefers-color-scheme: light) {
4 :root {
5+ --main-hue: 250;
6 --white: #6a737d;
7 --code: #fff8d3;
8 --code-border: #f0d547;
9@@ -34,6 +35,7 @@
10
11 @media (prefers-color-scheme: dark) {
12 :root {
13+ --main-hue: 250;
14 --white: #f2f2f2;
15 --code: #414558;
16 --code-border: #252525;
17@@ -271,6 +273,23 @@ figure {
18 margin: 0;
19 }
20
21+.btn-link {
22+ border: 3px solid hsl(var(--main-hue), 92%, 66%);
23+ background-color: hsl(var(--main-hue), 92%, 66%);
24+ padding: 0.5rem 1rem;
25+ border-radius: 0.25rem;
26+ box-shadow: 0px 1px 2px 0px black;
27+ color: var(--white);
28+ text-decoration: none;
29+ font-weight: bold;
30+}
31+
32+.btn-link:visited,
33+.btn-link:visited:hover,
34+.btn-link:hover {
35+ color: var(--white);
36+}
37+
38 .post-date {
39 width: 130px;
40 }
41@@ -319,6 +338,10 @@ figure {
42 display: inline;
43 }
44
45+.inline-block {
46+ display: inline-block;
47+}
48+
49 .flex {
50 display: flex;
51 }