feat: Add coller la petite post
Signed-off-by: Julien Riou <julien@riou.xyz>
This commit is contained in:
parent
f0cab2f7c3
commit
47ca98cc16
6 changed files with 753 additions and 0 deletions
207
content/blog/coller-la-petite.md
Normal file
207
content/blog/coller-la-petite.md
Normal file
|
@ -0,0 +1,207 @@
|
|||
+++
|
||||
title = 'Coller (la petite)'
|
||||
date = 2025-08-21T20:00:00+02:00
|
||||
+++
|
||||
|
||||
At the beginning of my career, we used e-mails to communicate with co-workers.
|
||||
We could explain an issue in details with a very long e-mail, but it was hard
|
||||
to follow live discussions. For synchronous communication, we used phones. We
|
||||
had to constantly switch between both systems. Have you ever received a call
|
||||
saying "have you seen my last e-mail?".
|
||||
|
||||
Then instant messaging systems came to fill the gap. There was
|
||||
[MSN](https://en.m.wikipedia.org/wiki/MSN_Messenger) at home to chat with your
|
||||
friends. Or XMPP at work. I have used this protocol for 10 years. OK there was
|
||||
no gif, limited support of emojis and you can't call your colleagues but it
|
||||
could ran everywhere, even on a [solarpunk phone](http://compost.party/).
|
||||
|
||||
Nowadays, we have Slack, Webex, Microsoft Teams, Discord. Maybe you have the
|
||||
chance to use an open source solution like Matrix or Mattermost. The decision
|
||||
to use one of another has probably been made before you joined the company and
|
||||
you don't have enough power to introduce a major change because communication
|
||||
systems are central to companies. So we use Webex at work. We have to deal with
|
||||
all the limitations. One of them is the ability to paste long text without
|
||||
polluting the feed.
|
||||
|
||||
# Pastebin service
|
||||
|
||||
In french, "paste" could be translated to "coller". One day, I decided to start
|
||||
my own pastebin service based on [sticky
|
||||
notes](https://github.com/sayakb/sticky-notes). I have ordered a VPS, an
|
||||
_internal_ top-level domain, restricted network access. The _coller la petite_
|
||||
service was born (in reference to [this
|
||||
song](https://genius.com/Franko-coller-la-petite-lyrics)). The name was fun.
|
||||
The service was useful. I did no marketing campaign around it because I knew it
|
||||
could be used to store sensible data. I have contacted our security team to
|
||||
tell them the service existed and told every user that it's an **external**
|
||||
service with no service level agreement (_best effort_ mode).
|
||||
|
||||
This website had little to no maintenance. Upgrade Debian packages, dump and
|
||||
restore the database on a new major version every year, easy peasy. For fun, I
|
||||
have created a Perl client to learn this language (because I had to). Then a
|
||||
more portable Go client was created by one of my team mates. Why do we need
|
||||
clients when there's already a website? Because sometimes we prefer to use a
|
||||
CLI and not a browser to share the content of our clipboard or the content of a
|
||||
file.
|
||||
|
||||
The service became so popular that the need was proven. The team responsible
|
||||
for managing collaboration tools came to me to see how could we move this
|
||||
external service and make it internal: hosted on private services with the
|
||||
enterprise security layer on top of it. I have explained the history, how it
|
||||
works and how I see the future to multiple people of that team. There is a high
|
||||
turnover rate apparently. Now I'm sick of explaining it over and over again.
|
||||
|
||||
# What's the problem?
|
||||
|
||||
There are multiple issues with this service today. Sticky notes is not
|
||||
maintained anymore. It's written in PHP which makes it harder for us to fork,
|
||||
contribute and deploy. Last but not least, there is no encryption.
|
||||
|
||||
# PrivateBin
|
||||
|
||||
The most popular and secure pastebin service out there is
|
||||
[PrivateBin](https://privatebin.info/). They take security very seriously. The
|
||||
content is encrypted from the browser so the hosting provider never knows
|
||||
what's inside. While this solution is awesome for the security, it makes it
|
||||
harder to use on a day to day basis. We can't use [curl](https://curl.se/) to
|
||||
download the raw content of a note. I get the argument of users always making
|
||||
the poorest choice but I don't want to set up a second solution to fill the gap
|
||||
or telling people to deal with it. I want a single solution for doing both. I
|
||||
want to paste public data. I also want to paste and download snippets securely.
|
||||
|
||||
I also don't like long URLs because, unlike Mastodon where size doesn't matter,
|
||||
they tend to bloat the message on those platforms. The fact that you have to
|
||||
use an URL shortener in the
|
||||
[FAQ](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#the-url-is-so-long-cant-i-just-use-an-url-shortener)
|
||||
says it all.
|
||||
|
||||
# Coller
|
||||
|
||||
So I created a pastebin server and clients to create and read notes. Yep. Yet
|
||||
another pastebin solution. Not because we definitely needed one more, but
|
||||
because I use it daily, I like simplicity, usability, security and I love doing
|
||||
side projects in Go.
|
||||
|
||||
## Features
|
||||
|
||||
- Website
|
||||
- Raw content by default
|
||||
- Compatible with curl
|
||||
- Encryption with password
|
||||
- Compression on the backend
|
||||
- Automatic retention
|
||||
- PostgreSQL or sqlite database
|
||||
- CLI to create notes ("coller") from the clipboard or a file
|
||||
- CLI to read encrypted notes ("copier")
|
||||
- Easy to deploy
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Encryption levels
|
||||
|
||||
## No encryption
|
||||
|
||||
[It starts with](https://www.youtube.com/watch?v=eVTXPUF4Oz4) no encryption.
|
||||
Sounds crazy in a world where everything has to be encrypted, right? Not so
|
||||
crazy. Sometimes, we just want to paste a bunch of characters that we could
|
||||
have been said publicly on social networks and that's fine. The URL will be
|
||||
very short and easy to share.
|
||||
|
||||
## Server side encryption
|
||||
|
||||
The next level is to use basic encryption by letting the website generate a
|
||||
password for you. You click on a checkbox and the content of the note is
|
||||
encrypted before being stored. The encryption key is the password. You can
|
||||
choose to keep the key for yourself and only you and the server know it. Or
|
||||
share it to other people you trust. Anyway, the server knows it.
|
||||
|
||||
## Server side encryption with user key
|
||||
|
||||
Same solution as the server side encryption but this time, the user defines the
|
||||
password. Security could be lowered if the key is weak (too short, words from
|
||||
the dictionary) when the server will always generate strong enough keys. In the
|
||||
end, the server still knows your choosen key.
|
||||
|
||||
## Client side encryption
|
||||
|
||||
If you don't trust the server but you want to use the service anyway, you could
|
||||
tell the client to encrypt the content before sending it to the server. The
|
||||
client will generate a random (strong) key. This time, the key is private. Only
|
||||
you knows it. You can do whatever you want with the key, but you should keep it
|
||||
to yourself or share it carefully.
|
||||
|
||||
Accessing the encrypted note using the password in the URL will make the server
|
||||
aware of your password. You should use the "copier" client to download the
|
||||
encrypted note and decrypt outside of the server.
|
||||
|
||||
## Client side encryption with user key
|
||||
|
||||
Same as client side encryption but you can choose the password you want.
|
||||
|
||||
## Encryption summary
|
||||
|
||||
A picture is worth a thousand words:
|
||||
|
||||

|
||||
|
||||
# What to choose?
|
||||
|
||||
|
||||
| Level | Usability | Confidentiality |
|
||||
| ---------------------- | --------- | --------------- |
|
||||
| No encryption | ⭐⭐ | ⭐ |
|
||||
| Server-side encryption | ⭐⭐⭐ | ⭐⭐ |
|
||||
| Client-side encryption | ⭐ | ⭐⭐⭐ |
|
||||
|
||||
If you can use the copier client (CLI) and keep the password for yourself, go
|
||||
for it. It's the most secure solution. If you can't, the website is designed to
|
||||
use the server-side encryption with a strong generated password.
|
||||
|
||||
# Cipher
|
||||
|
||||
The most famous cipher is
|
||||
[AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) 256 with
|
||||
[GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode). It would have been
|
||||
simpler to implement
|
||||
[CBC](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation) like
|
||||
[pgBackRest](https://pgbackrest.org/) but the mode has been removed from TLS
|
||||
1.3 for several flaws. I decided to use
|
||||
[XChaha20-Poly1305](https://pkg.go.dev/golang.org/x/crypto/chacha20) because it
|
||||
reminds me [this banger](https://www.youtube.com/watch?v=DGsL8hA-1rE) from this
|
||||
year's Eurovision and I wanted to use something different. Don't worry, this
|
||||
cipher is [one of the most secure in the
|
||||
world](https://wiki.mozilla.org/Security/Server_Side_TLS).
|
||||
|
||||
# Source code
|
||||
|
||||
The source code is available [on my forgejo](https://git.riou.xyz/jriou/coller)
|
||||
instance under the permissive
|
||||
[MIT](https://git.riou.xyz/jriou/coller/src/branch/main/LICENSE) license.
|
||||
|
||||
# Conclusion
|
||||
|
||||
This little side project turned into a fully functional pastebin solution. It
|
||||
was also a great excuse to implement encryption in Go. If you find a security
|
||||
issue or want to implement a feature or fix bugs, contributions are welcomed.
|
BIN
static/coller/coller-cli.png
Normal file
BIN
static/coller/coller-cli.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
BIN
static/coller/coller-created.png
Normal file
BIN
static/coller/coller-created.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
542
static/coller/coller-encryption.excalidraw
Normal file
542
static/coller/coller-encryption.excalidraw
Normal file
|
@ -0,0 +1,542 @@
|
|||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [
|
||||
{
|
||||
"id": "yKFkkk4Yj5Zauko2WKG-d",
|
||||
"type": "rectangle",
|
||||
"x": 555.25,
|
||||
"y": 331,
|
||||
"width": 179.99999999999997,
|
||||
"height": 212,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "as",
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 760287448,
|
||||
"version": 140,
|
||||
"versionNonce": 1684089560,
|
||||
"isDeleted": false,
|
||||
"boundElements": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "sNYRyKWZkChLuCLsm0VYi"
|
||||
}
|
||||
],
|
||||
"updated": 1755789246679,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "sNYRyKWZkChLuCLsm0VYi",
|
||||
"type": "text",
|
||||
"x": 592.5250015258789,
|
||||
"y": 413,
|
||||
"width": 105.44999694824219,
|
||||
"height": 48,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "at",
|
||||
"roundness": null,
|
||||
"seed": 1834769832,
|
||||
"version": 114,
|
||||
"versionNonce": 27660248,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1755789246679,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Server\n(collerd)",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "yKFkkk4Yj5Zauko2WKG-d",
|
||||
"originalText": "Server\n(collerd)",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.2
|
||||
},
|
||||
{
|
||||
"id": "Kc7_mrdgSLrf6jNGvILS3",
|
||||
"type": "rectangle",
|
||||
"x": 1175.75,
|
||||
"y": 554,
|
||||
"width": 141.00000000000003,
|
||||
"height": 156,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "au",
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 545326552,
|
||||
"version": 444,
|
||||
"versionNonce": 652351192,
|
||||
"isDeleted": false,
|
||||
"boundElements": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "CepAMVAuKJpOQI-AzdS4a"
|
||||
},
|
||||
{
|
||||
"id": "ocyk_yJCiGwmnkR55FoTJ",
|
||||
"type": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "BkLH2VDz86-eIlEB8PA3M",
|
||||
"type": "arrow"
|
||||
}
|
||||
],
|
||||
"updated": 1755789238168,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "CepAMVAuKJpOQI-AzdS4a",
|
||||
"type": "text",
|
||||
"x": 1211.099998474121,
|
||||
"y": 608,
|
||||
"width": 70.30000305175781,
|
||||
"height": 48,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "av",
|
||||
"roundness": null,
|
||||
"seed": 51546840,
|
||||
"version": 421,
|
||||
"versionNonce": 1445250264,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 1755789187612,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Client\n(web)",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "Kc7_mrdgSLrf6jNGvILS3",
|
||||
"originalText": "Client\n(web)",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.2
|
||||
},
|
||||
{
|
||||
"id": "iEFEFdLxEQSOj9PksxsbT",
|
||||
"type": "text",
|
||||
"x": 499.25,
|
||||
"y": 584,
|
||||
"width": 257.76666259765625,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "aw",
|
||||
"roundness": null,
|
||||
"seed": 1132344792,
|
||||
"version": 117,
|
||||
"versionNonce": 151768792,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1755788934562,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "https://coller/note-id",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "https://coller/note-id",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.2
|
||||
},
|
||||
{
|
||||
"id": "8nsGIc_KHl1TFnpg6ejUo",
|
||||
"type": "text",
|
||||
"x": 499.3666687011719,
|
||||
"y": 661,
|
||||
"width": 399.9333190917969,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "ax",
|
||||
"roundness": null,
|
||||
"seed": 1693301208,
|
||||
"version": 179,
|
||||
"versionNonce": 1850772184,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 1755789170471,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "https://coller/note-id/password 🔑",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "https://coller/note-id/password 🔑",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.2
|
||||
},
|
||||
{
|
||||
"id": "_z-MUabVHx5E8_l4BWJ1z",
|
||||
"type": "line",
|
||||
"x": 1045.25,
|
||||
"y": 274,
|
||||
"width": 0,
|
||||
"height": 461,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "dotted",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "ay",
|
||||
"roundness": {
|
||||
"type": 2
|
||||
},
|
||||
"seed": 228580056,
|
||||
"version": 141,
|
||||
"versionNonce": 528663256,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1755788967611,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
461
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": null,
|
||||
"endBinding": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": null,
|
||||
"polygon": false
|
||||
},
|
||||
{
|
||||
"id": "JouKOO4uPqBFGr6XZX_py",
|
||||
"type": "rectangle",
|
||||
"x": 1174.75,
|
||||
"y": 277,
|
||||
"width": 141.00000000000003,
|
||||
"height": 156,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "az",
|
||||
"roundness": {
|
||||
"type": 3
|
||||
},
|
||||
"seed": 496191400,
|
||||
"version": 519,
|
||||
"versionNonce": 1511097560,
|
||||
"isDeleted": false,
|
||||
"boundElements": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "VEdH7Z2EKX0em85vXetqX"
|
||||
}
|
||||
],
|
||||
"updated": 1755789290129,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "VEdH7Z2EKX0em85vXetqX",
|
||||
"type": "text",
|
||||
"x": 1198.3833351135254,
|
||||
"y": 331,
|
||||
"width": 93.73332977294922,
|
||||
"height": 48,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "b00",
|
||||
"roundness": null,
|
||||
"seed": 1578898088,
|
||||
"version": 520,
|
||||
"versionNonce": 1421427624,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 1755789189329,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Client\n(copier)",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "JouKOO4uPqBFGr6XZX_py",
|
||||
"originalText": "Client\n(copier)",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.2
|
||||
},
|
||||
{
|
||||
"id": "ktbpPk_s1UpLcnSnu93ef",
|
||||
"type": "text",
|
||||
"x": 1183.2833404541016,
|
||||
"y": 457,
|
||||
"width": 411.6499938964844,
|
||||
"height": 24,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "b03",
|
||||
"roundness": null,
|
||||
"seed": 1415160792,
|
||||
"version": 493,
|
||||
"versionNonce": 21443032,
|
||||
"isDeleted": false,
|
||||
"boundElements": [],
|
||||
"updated": 1755789281126,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "copier -password PASSWORD 🔑 <URL> ",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null,
|
||||
"originalText": "copier -password PASSWORD 🔑 <URL> ",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.2
|
||||
},
|
||||
{
|
||||
"id": "6CQhcN4aPSfHv0u_lyIDx",
|
||||
"type": "arrow",
|
||||
"x": 1161.25,
|
||||
"y": 477,
|
||||
"width": 361,
|
||||
"height": 104,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "b04",
|
||||
"roundness": {
|
||||
"type": 2
|
||||
},
|
||||
"seed": 2133643432,
|
||||
"version": 109,
|
||||
"versionNonce": 574848728,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1755789311725,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-361,
|
||||
104
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": null,
|
||||
"endBinding": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": false
|
||||
},
|
||||
{
|
||||
"id": "ocyk_yJCiGwmnkR55FoTJ",
|
||||
"type": "arrow",
|
||||
"x": 1158.25,
|
||||
"y": 664,
|
||||
"width": 237,
|
||||
"height": 0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "b05",
|
||||
"roundness": {
|
||||
"type": 2
|
||||
},
|
||||
"seed": 1443036840,
|
||||
"version": 52,
|
||||
"versionNonce": 1027734696,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1755789226112,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-237,
|
||||
0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "Kc7_mrdgSLrf6jNGvILS3",
|
||||
"focus": -0.41025641025641013,
|
||||
"gap": 17.5
|
||||
},
|
||||
"endBinding": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": false
|
||||
},
|
||||
{
|
||||
"id": "BkLH2VDz86-eIlEB8PA3M",
|
||||
"type": "arrow",
|
||||
"x": 1153.25,
|
||||
"y": 599,
|
||||
"width": 351,
|
||||
"height": 0,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"index": "b06",
|
||||
"roundness": {
|
||||
"type": 2
|
||||
},
|
||||
"seed": 2066376872,
|
||||
"version": 194,
|
||||
"versionNonce": 2036082856,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1755789319209,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-351,
|
||||
0
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "Kc7_mrdgSLrf6jNGvILS3",
|
||||
"focus": 0.42307692307692313,
|
||||
"gap": 22.5
|
||||
},
|
||||
"endBinding": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": false
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 20,
|
||||
"gridStep": 5,
|
||||
"gridModeEnabled": false,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"lockedMultiSelections": {}
|
||||
},
|
||||
"files": {}
|
||||
}
|
4
static/coller/coller-encryption.svg
Normal file
4
static/coller/coller-encryption.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 22 KiB |
BIN
static/coller/coller-index.png
Normal file
BIN
static/coller/coller-index.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
Loading…
Add table
Add a link
Reference in a new issue