<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Julien Béranger</title>
    <description>The latest articles on DEV Community by Julien Béranger (@julienbrg).</description>
    <link>https://dev.to/julienbrg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F836545%2Fb4258c1e-10a3-45a7-8e6a-dccfaa1df468.jpeg</url>
      <title>DEV Community: Julien Béranger</title>
      <link>https://dev.to/julienbrg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/julienbrg"/>
    <language>en</language>
    <item>
      <title>Publish encrypted, shareable docs from your terminal (or Claude Code) with Fileverse</title>
      <dc:creator>Julien Béranger</dc:creator>
      <pubDate>Wed, 08 Jul 2026 15:30:43 +0000</pubDate>
      <link>https://dev.to/julienbrg/publish-encrypted-shareable-docs-from-your-terminal-or-claude-code-with-fileverse-h3h</link>
      <guid>https://dev.to/julienbrg/publish-encrypted-shareable-docs-from-your-terminal-or-claude-code-with-fileverse-h3h</guid>
      <description>&lt;p&gt;I often need to publish some notes somewhere, just to keep it or to hand it to someone, and I always found it painful to interrupt my work, go create a Google doc and copy-paste the content. I'm lazy as a good dev should, so here I am with the solution. &lt;/p&gt;

&lt;p&gt;In you terminal, you type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;share SECURITY_AUDIT_JAN_15.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or in Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/share that doc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it returns this shareable link: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.fileverse.io/0x21b9d1591b1440BA8D3f2470E18f7d9f29e4E345/10#key=-GcNYlM5tVR9Kh2LNfFMZBFgGkgMLR6gHXIFKrtIZ0kCvAX5spcEpV-clnfqjVzE" rel="noopener noreferrer"&gt;https://docs.fileverse.io/0x21b9d1591b1440BA8D3f2470E18f7d9f29e4E345/10#key=-GcNYlM5tVR9Kh2LNfFMZBFgGkgMLR6gHXIFKrtIZ0kCvAX5spcEpV-clnfqjVzE&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don't even know if this is feasible with Google Docs and I don't really care because, in case you haven't noticed yet, &lt;a href="https://fileverse.io" rel="noopener noreferrer"&gt;Fileverse&lt;/a&gt; is simply the best alternative in town.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ddocs.new" rel="noopener noreferrer"&gt;dDocs&lt;/a&gt; are &lt;strong&gt;end-to-end encrypted documents&lt;/strong&gt;: content is encrypted client-side before it ever leaves your machine, references are anchored onchain, and sharing works through a link that carries the decryption key in the URL fragment (the part after &lt;code&gt;#&lt;/code&gt;, which browsers never send to servers). No account required for readers. No ads, no tracking, no platform lock-in.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/fileverse/api" rel="noopener noreferrer"&gt;Fileverse API&lt;/a&gt; makes this scriptable: it's a small &lt;strong&gt;self-hosted server&lt;/strong&gt; you run locally. By the end of this tutorial you'll be able to do this from any project on your machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;share SECURITY_AUDIT_JAN_15.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and get back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Published: SECURITY_AUDIT_JAN_15.md
ddocId: 5dKEgEgsSbFVrCsmSv4yf4
link: [https://docs.fileverse.io/0x21b9d1591b1440BA8D3f2470E18f7d9f29e4E345/10#key=-GcNYlM5tVR9Kh2LNfFMZBFgGkgMLR6gHXIFKrtIZ0kCvAX5spcEpV-clnfqjVzE](https://docs.fileverse.io/0x21b9d1591b1440BA8D3f2470E18f7d9f29e4E345/10#key=-GcNYlM5tVR9Kh2LNfFMZBFgGkgMLR6gHXIFKrtIZ0kCvAX5spcEpV-clnfqjVzE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That link is instantly shareable with anyone. Bonus: it also works as a &lt;code&gt;/share&lt;/code&gt; slash command in &lt;strong&gt;Claude Code&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; you do NOT deploy this API anywhere. It runs on &lt;code&gt;127.0.0.1&lt;/code&gt;. That's the point — encryption happens on your device.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js v18+&lt;/strong&gt; (&lt;code&gt;node -v&lt;/code&gt; to check)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;macOS or Linux&lt;/strong&gt; with &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, and &lt;code&gt;python3&lt;/code&gt; (all preinstalled on macOS)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;git&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Optionally: &lt;a href="https://docs.claude.com/en/docs/claude-code/overview" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; for the slash-command part&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1 — Fileverse signup
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://ddocs.new" rel="noopener noreferrer"&gt;ddocs.new&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create your account / portal (a portal is your onchain document space)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2 — Get your API key
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In ddocs.new, open &lt;strong&gt;Settings&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;Developer Mode&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Generate your &lt;strong&gt;API key&lt;/strong&gt; and copy it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Treat this key like a password. It never lives in a script or in git — only in a local config file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Install and run the local API server
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @fileverse/api
fileverse-api &lt;span class="nt"&gt;--apiKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first run fetches your key data, writes config to &lt;code&gt;~/.fileverse/.env&lt;/code&gt;, runs database migrations, and starts the API server (port 8001) plus a background worker that publishes your docs onchain.&lt;/p&gt;

&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;✓ Fileverse API is running!

  API Server: http://127.0.0.1:8001
  Worker:     Active
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Troubleshooting: &lt;code&gt;ERR_MODULE_NOT_FOUND: Cannot find package 'y-protocols'&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;At the time of writing, the npm package can hit a missing-dependency error on some setups (like mine!). Fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; y-protocols yjs
npm uninstall &lt;span class="nt"&gt;-g&lt;/span&gt; @fileverse/api
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @fileverse/api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;code&gt;fileverse-api --apiKey="..."&lt;/code&gt; again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Install &lt;code&gt;share&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Clone the &lt;a href="https://github.com/julienbrg/dotfiles" rel="noopener noreferrer"&gt;dotfiles repo&lt;/a&gt; and run its installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/julienbrg/dotfiles.git ~/dotfiles
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/dotfiles
./install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;install.sh&lt;/code&gt; symlinks two files into place: the &lt;code&gt;share&lt;/code&gt; command (into &lt;code&gt;~/.local/bin/share&lt;/code&gt;) and the Claude Code &lt;code&gt;/share&lt;/code&gt; command (into &lt;code&gt;~/.claude/commands/share.md&lt;/code&gt;). It's safe to re-run — anything already at those paths gets backed up as &lt;code&gt;.bak&lt;/code&gt; first.&lt;/p&gt;

&lt;p&gt;Make sure &lt;code&gt;~/.local/bin&lt;/code&gt; is on your PATH (zsh shown; use &lt;code&gt;~/.bashrc&lt;/code&gt; for bash):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH="$HOME/.local/bin:$PATH"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.zshrc
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What &lt;code&gt;share&lt;/code&gt; does behind the scenes
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;share &amp;lt;file&amp;gt;&lt;/code&gt; is a single command, but it handles the whole publish flow for you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reads your API key&lt;/strong&gt; from &lt;code&gt;~/.fileverse/.env&lt;/code&gt; — nothing is ever hardcoded or passed on the command line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checks whether the local server is already running.&lt;/strong&gt; If not, it starts &lt;code&gt;fileverse-api&lt;/code&gt; itself and waits (up to 45s) for it to become healthy. If it &lt;em&gt;was&lt;/em&gt; already running, &lt;code&gt;share&lt;/code&gt; leaves it alone — it only ever stops a server it started itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uploads the file&lt;/strong&gt; to the local API, which encrypts it client-side and kicks off the onchain sync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polls until the sync completes&lt;/strong&gt;, retrying once automatically if a sync event fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prints the shareable link&lt;/strong&gt;, then shuts back down any server it started in step 2.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It also remembers every file it publishes (in &lt;code&gt;~/.fileverse/shared.tsv&lt;/code&gt;), so re-sharing the same file &lt;em&gt;updates&lt;/em&gt; the existing doc instead of creating a new one (more on this in Step 6).&lt;/p&gt;

&lt;p&gt;A couple of details worth knowing if something goes sideways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parsing is done in Python rather than &lt;code&gt;jq&lt;/code&gt;, because the API echoes your document content back in its response, and markdown can contain raw control characters that break &lt;code&gt;jq&lt;/code&gt;. Python's JSON parser handles it fine.&lt;/li&gt;
&lt;li&gt;If a run gets interrupted mid-sync, nothing is lost — sync state lives in a local SQLite database, so the next &lt;code&gt;share&lt;/code&gt; run just picks up where it left off.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5 — Verify setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Server config exists?&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.fileverse/.env        &lt;span class="c"&gt;# should show API_KEY=... and DB_PATH=...&lt;/span&gt;

&lt;span class="c"&gt;# 2. Command is found and executable?&lt;/span&gt;
which share                  &lt;span class="c"&gt;# → /Users/you/.local/bin/share&lt;/span&gt;

&lt;span class="c"&gt;# 3. Publish something!&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/any-project
share SECURITY_AUDIT_JAN_15.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Created ddoc o84yyFHUropP47AYiAqpXe, waiting for onchain sync...
Published: SECURITY_AUDIT_JAN_15.md
ddocId: o84yyFHUropP47AYiAqpXe
link: [https://docs.fileverse.io/0x21b9d1591b1440BA8D3f2470E18f7d9f29e4E345/10#key=-GcNYlM5tVR9Kh2LNfFMZBFgGkgMLR6gHXIFKrtIZ0kCvAX5spcEpV-clnfqjVzE](https://docs.fileverse.io/0x21b9d1591b1440BA8D3f2470E18f7d9f29e4E345/10#key=-GcNYlM5tVR9Kh2LNfFMZBFgGkgMLR6gHXIFKrtIZ0kCvAX5spcEpV-clnfqjVzE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the link in a browser — your document, decrypted client-side.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sharing model:&lt;/strong&gt; the link is &lt;strong&gt;view-only, for everyone including you&lt;/strong&gt; — it carries the &lt;em&gt;decryption&lt;/em&gt; key, not edit rights. Anyone with the link can read the doc, so treat the link itself as the secret, and remember there's no easy "unshare" once it's out.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One thing to know: docs published through the API don't join your regular file list at &lt;a href="https://ddocs.new" rel="noopener noreferrer"&gt;ddocs.new&lt;/a&gt; — once you've opened a link they show up under &lt;strong&gt;"Recently accessed"&lt;/strong&gt;, but view-only, same as for anyone else with the link. Their edit keys live only in the local API database (&lt;code&gt;~/.fileverse/fileverse-api.db&lt;/code&gt;), so the browser app can't edit them. To change a published doc, update it from your machine — see the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6 — Updating a published doc
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;share&lt;/code&gt; keeps a small ledger of everything it has published (&lt;code&gt;~/.fileverse/shared.tsv&lt;/code&gt;). Re-run it on a file you've already shared, and instead of creating a new doc it updates the existing one in place — &lt;strong&gt;the link stays the same&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;share SECURITY_AUDIT_JAN_15.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Already published as ddoc o84yyFHUropP47AYiAqpXe — updating it, link stays the same.
Updated ddoc o84yyFHUropP47AYiAqpXe, waiting for on-chain sync...
Updated: SECURITY_AUDIT_JAN_15.md
ddocId: o84yyFHUropP47AYiAqpXe
link: https://docs.fileverse.io/0x21b9...#key=zMUB4xQb...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Files are tracked by absolute path, so a &lt;code&gt;README.md&lt;/code&gt; in one project and a &lt;code&gt;README.md&lt;/code&gt; in another are (correctly) two different docs.&lt;/p&gt;

&lt;p&gt;For a doc published before the ledger existed, pass its &lt;code&gt;ddocId&lt;/code&gt; explicitly once — that updates the doc &lt;em&gt;and&lt;/em&gt; records the mapping, so from then on the plain &lt;code&gt;share &amp;lt;file&amp;gt;&lt;/code&gt; does the right thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;share SECURITY_AUDIT_JAN_15.md o84yyFHUropP47AYiAqpXe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lost a ddocId? The API package also installs a &lt;code&gt;ddctl&lt;/code&gt; CLI — &lt;code&gt;ddctl list&lt;/code&gt; shows everything you've published, and &lt;code&gt;ddctl update &amp;lt;ddocId&amp;gt;&lt;/code&gt; even opens the doc in your &lt;code&gt;$EDITOR&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7 — Claude Code &lt;code&gt;/share&lt;/code&gt; command
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;install.sh&lt;/code&gt; already symlinked this in Step 4, so &lt;code&gt;/share&lt;/code&gt; works out of the box in every project. It's a markdown file (&lt;code&gt;~/.claude/commands/share.md&lt;/code&gt;) that delegates entirely to the &lt;code&gt;share&lt;/code&gt; script — Claude runs one Bash call, and only the ~4-line result (not your whole document) enters the conversation. Faster, cheaper, and deterministic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/share SECURITY_AUDIT_JAN_15.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Claude Code prompts for permission the first time, pick &lt;strong&gt;"Yes, and don't ask again"&lt;/strong&gt; — that's expected the first time you use any new command, and it won't ask again after that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it your own
&lt;/h2&gt;

&lt;p&gt;This tutorial points at my own dotfiles repo so you can try &lt;code&gt;share&lt;/code&gt; immediately. To keep your own copy under your control long-term: fork &lt;a href="https://github.com/julienbrg/dotfiles" rel="noopener noreferrer"&gt;julienbrg/dotfiles&lt;/a&gt; (or just copy &lt;code&gt;bin/share&lt;/code&gt; and &lt;code&gt;claude/commands/share.md&lt;/code&gt; into your own dotfiles repo), point &lt;code&gt;install.sh&lt;/code&gt; at your fork, and push. Same &lt;code&gt;install.sh&lt;/code&gt; pattern works on any new machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@github.com:YOUR_USERNAME/dotfiles.git ~/dotfiles
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/dotfiles &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./install.sh
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @fileverse/api
fileverse-api &lt;span class="nt"&gt;--apiKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_KEY"&lt;/span&gt;   &lt;span class="c"&gt;# regenerate a key per machine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⚠️ The one rule that matters:&lt;/strong&gt; never commit secrets. &lt;code&gt;share&lt;/code&gt; reads the API key from &lt;code&gt;~/.fileverse/.env&lt;/code&gt; at runtime — that file stays &lt;em&gt;outside&lt;/em&gt; the repo, and &lt;code&gt;install.sh&lt;/code&gt; never touches it. If you ever paste a key into a chat, an issue, or a commit, regenerate it in ddocs.new Settings → Developer Mode. Keys are cheap; leaked keys are not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API key + config&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.fileverse/.env&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secrets, never in git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;share&lt;/code&gt; script&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;dotfiles/bin/share&lt;/code&gt; → symlinked to &lt;code&gt;~/.local/bin/share&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Publish from any terminal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude command&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;dotfiles/claude/commands/share.md&lt;/code&gt; → symlinked to &lt;code&gt;~/.claude/commands/share.md&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/share &amp;lt;file&amp;gt; [ddocId]&lt;/code&gt; in Claude Code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dDocs UI&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ddocs.new" rel="noopener noreferrer"&gt;ddocs.new&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Account + API keys (API-published docs appear view-only, under "Recently accessed")&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Happy sharing! 🚀&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built with the &lt;a href="https://github.com/fileverse/api" rel="noopener noreferrer"&gt;Fileverse API&lt;/a&gt; (AGPL-3.0). Not affiliated with Fileverse — just a fan of encrypted, self-hosted publishing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>claude</category>
      <category>tutorial</category>
      <category>bash</category>
    </item>
    <item>
      <title>Getting Started with Genji: A Web3 App Starter with Passkey Authentication</title>
      <dc:creator>Julien Béranger</dc:creator>
      <pubDate>Tue, 03 Mar 2026 11:50:46 +0000</pubDate>
      <link>https://dev.to/julienbrg/getting-started-with-genji-your-web3-adventure-begins-here-3oec</link>
      <guid>https://dev.to/julienbrg/getting-started-with-genji-your-web3-adventure-begins-here-3oec</guid>
      <description>&lt;p&gt;Hello Anon!&lt;/p&gt;

&lt;p&gt;Genji is a Next.js starter template that makes it easy to build Web3 apps with passkey authentication. This template gives you a production-ready foundation that handles security and accessibility while keeping user privacy front and center.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in it?
&lt;/h2&gt;

&lt;p&gt;Genji integrates the following technologies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://w3pk.w3hc.org/" rel="noopener noreferrer"&gt;w3pk&lt;/a&gt;&lt;/strong&gt; — Passkey authentication framework&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;&lt;/strong&gt; — React framework for production apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.ethers.org/" rel="noopener noreferrer"&gt;Ethers&lt;/a&gt;&lt;/strong&gt; — Web3 library for blockchain interaction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://chakra-ui.com/" rel="noopener noreferrer"&gt;Chakra UI&lt;/a&gt;&lt;/strong&gt; — Component library for user interface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WCAG 2.1 AA compliance&lt;/strong&gt; — Ensures accessibility for everyone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the live demo at &lt;a href="https://genji.w3hc.org" rel="noopener noreferrer"&gt;https://genji.w3hc.org&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy-Friendly Architecture
&lt;/h2&gt;

&lt;p&gt;Genji takes a privacy-first approach to authentication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No password storage&lt;/strong&gt;: Passkeys eliminate the need for password databases, removing a common attack vector&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device-bound credentials&lt;/strong&gt;: Authentication credentials stay on the user's device and are never sent to servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No tracking&lt;/strong&gt;: No need for email addresses, phone numbers, or other personally identifiable information&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User control&lt;/strong&gt;: Users stay in full control of their credentials and can revoke access anytime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal data collection&lt;/strong&gt;: The app only stores the public key for each account, keeping things private while staying secure&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation and Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create Your Repository
&lt;/h3&gt;

&lt;p&gt;To create your own instance of the Genji template:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Head to the &lt;a href="https://github.com/w3hc/genji-passkey" rel="noopener noreferrer"&gt;Genji GitHub repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click "Use this template" to create a new repository&lt;/li&gt;
&lt;li&gt;Give your repository a name&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Customize the Template
&lt;/h3&gt;

&lt;p&gt;After cloning your repository, run the customization script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm customize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove default Genji branding&lt;/li&gt;
&lt;li&gt;Set up project-specific configurations&lt;/li&gt;
&lt;li&gt;Self-destruct when it's done&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Install Dependencies
&lt;/h3&gt;

&lt;p&gt;Install the required packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm i
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Start the Development Server
&lt;/h3&gt;

&lt;p&gt;Fire up the dev server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your app will be running at &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now log in and try signing a message on the homepage. Once you're logged in, you don't need to confirm anything to sign the message: w3pk handles it seamlessly using your passkey-derived wallet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Configure Security Level
&lt;/h3&gt;

&lt;p&gt;w3pk offers four security levels for passkey authentication. We recommend &lt;strong&gt;STANDARD&lt;/strong&gt;, which balances security and user experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PRIMARY&lt;/strong&gt;: Requires authentication for every single action, including transaction confirmations. Maximum security but requires the most user interaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STRICT&lt;/strong&gt;: Requires authentication for most operations. Good for high-security apps where user inconvenience is acceptable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STANDARD&lt;/strong&gt; (recommended): Requires authentication for login but keeps the session active for what comes next. Users don't need to re-authenticate for each transaction. This gives you the best balance between security and usability for most apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YOLO&lt;/strong&gt;: Minimal authentication requirements. Only for development or low-stakes apps where security isn't a primary concern.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;STANDARD is recommended because it cuts out repeated authentication while keeping solid security controls. Users can interact with your app smoothly without compromising account security.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are a few app examples &lt;a href="https://w3pk.w3hc.org/examples" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you need some inspiration. Genji can be used for all kinds of apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Passkey Authentication
&lt;/h2&gt;

&lt;p&gt;Passkeys are a modern authentication standard that use device-level security like biometric sensors (Face ID, fingerprint) or device PINs. This approach gets rid of traditional password management and seed phrase storage. The authentication process relies on public-key cryptography, where the private key stays securely on the user's device and is never transmitted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: Check out the &lt;code&gt;/docs&lt;/code&gt; folder for implementation context and design guidelines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Verification&lt;/strong&gt;: Set up &lt;a href="https://w3pk.w3hc.org/docs#build-verification" rel="noopener noreferrer"&gt;w3pk build verification&lt;/a&gt; to ensure code integrity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Inspection&lt;/strong&gt;: Enable &lt;a href="https://w3pk.w3hc.org/docs#security-inspection" rel="noopener noreferrer"&gt;security inspection&lt;/a&gt; so your users can verify what's happening in your app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility&lt;/strong&gt;: Keep WCAG 2.1 AA compliance throughout development&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Support and Resources
&lt;/h2&gt;

&lt;p&gt;Need help?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Submit issues on the &lt;a href="https://github.com/w3hc/genji-passkey" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Check the complete &lt;a href="https://w3pk.w3hc.org/docs" rel="noopener noreferrer"&gt;w3pk docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Contact &lt;a href="https://julienberanger.com/contact" rel="noopener noreferrer"&gt;Julien&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You now have everything you need to build Web3 apps using the Genji template with passkey authentication. The template gives you a privacy-focused, accessible, and secure foundation for building modern decentralized apps.&lt;/p&gt;

</description>
      <category>w3pk</category>
      <category>web3</category>
      <category>passkey</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Deploy a Next.js app to an Infomaniak Ubuntu VPS</title>
      <dc:creator>Julien Béranger</dc:creator>
      <pubDate>Mon, 20 Oct 2025 12:51:06 +0000</pubDate>
      <link>https://dev.to/julienbrg/deploy-a-nextjs-app-to-an-infomaniak-ubuntu-vps-2nod</link>
      <guid>https://dev.to/julienbrg/deploy-a-nextjs-app-to-an-infomaniak-ubuntu-vps-2nod</guid>
      <description>&lt;p&gt;This guide walks you through deploying a Next.js application to an Infomaniak Ubuntu VPS with automatic CI/CD using GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A domain name with DNS access&lt;/li&gt;
&lt;li&gt;A GitHub account&lt;/li&gt;
&lt;li&gt;VS Code installed on your local machine (for remote development)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  0. Order Your Infomaniak VPS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  0.1 Choose Your Plan
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://shop.infomaniak.com/order/select/vps_only" rel="noopener noreferrer"&gt;Infomaniak VPS Order Page&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recommended plan&lt;/strong&gt;: VPS Start at &lt;strong&gt;3.60 € / month&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;1 vCore&lt;/li&gt;
&lt;li&gt;2 GB RAM&lt;/li&gt;
&lt;li&gt;20 GB SSD&lt;/li&gt;
&lt;li&gt;Perfect for small to medium Next.js applications&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  0.2 VPS Configuration
&lt;/h3&gt;

&lt;p&gt;During the order process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Operating System&lt;/strong&gt;: Select &lt;strong&gt;Ubuntu 24.04 LTS&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Datacenter&lt;/strong&gt;: Choose the location closest to your users (Geneva or Zurich recommended)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH Key&lt;/strong&gt; (optional but recommended): Add your public SSH key for secure access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hostname&lt;/strong&gt;: Give your VPS a meaningful name (e.g., &lt;code&gt;my-nextjs-app&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  0.3 Complete Your Order
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Review your configuration&lt;/li&gt;
&lt;li&gt;Complete the payment process&lt;/li&gt;
&lt;li&gt;You'll receive an email with:

&lt;ul&gt;
&lt;li&gt;VPS IP address&lt;/li&gt;
&lt;li&gt;Root password (if you didn't add an SSH key)&lt;/li&gt;
&lt;li&gt;Access to Infomaniak Manager&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  0.4 Access Infomaniak Manager
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log in to &lt;a href="https://manager.infomaniak.com" rel="noopener noreferrer"&gt;manager.infomaniak.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Web &amp;amp; Domain&lt;/strong&gt; → &lt;strong&gt;VPS&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select your newly created VPS&lt;/li&gt;
&lt;li&gt;Note your VPS IP address (you'll need it throughout this guide)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Infomaniak Firewall Configuration
&lt;/h2&gt;

&lt;p&gt;Before connecting to your VPS, configure the firewall in the Infomaniak Manager interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 Access Firewall Settings
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In Infomaniak Manager, go to your VPS&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Firewall&lt;/strong&gt; in the left sidebar&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add Rule&lt;/strong&gt; to create new firewall rules&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1.2 Required Firewall Rules
&lt;/h3&gt;

&lt;p&gt;Create the following rules (in this order):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 1: SSH Access&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol&lt;/strong&gt;: TCP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port&lt;/strong&gt;: 22&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt;: Your IP address (or 0.0.0.0/0 for access from anywhere)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Accept&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: SSH access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule 2: HTTP Access&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol&lt;/strong&gt;: TCP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port&lt;/strong&gt;: 80&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt;: 0.0.0.0/0 (anywhere)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Accept&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: HTTP traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule 3: HTTPS Access&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol&lt;/strong&gt;: TCP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port&lt;/strong&gt;: 443&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt;: 0.0.0.0/0 (anywhere)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Accept&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: HTTPS traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule 4: Ping/ICMP (optional)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol&lt;/strong&gt;: ICMP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt;: 0.0.0.0/0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: Accept&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: Ping requests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1.3 Verify Firewall Status
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ensure the firewall is &lt;strong&gt;Enabled&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Rules should be listed in order&lt;/li&gt;
&lt;li&gt;Save your configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The Infomaniak firewall works at the network level before your VPS. You'll also configure UFW (Ubuntu's firewall) later for an additional layer of security.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. VS Code SSH Configuration
&lt;/h2&gt;

&lt;p&gt;Configure VS Code for remote development on your VPS.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Install Remote SSH Extension
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open VS Code&lt;/li&gt;
&lt;li&gt;Go to Extensions (Ctrl+Shift+X or Cmd+Shift+X)&lt;/li&gt;
&lt;li&gt;Search for "Remote - SSH"&lt;/li&gt;
&lt;li&gt;Install the extension by Microsoft&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.2 Generate SSH Key (if you don't have one)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;On macOS/Linux:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"your-email@example.com"&lt;/span&gt;
&lt;span class="c"&gt;# Press Enter to accept default location (~/.ssh/id_ed25519)&lt;/span&gt;
&lt;span class="c"&gt;# Set a passphrase (optional but recommended)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Windows (PowerShell):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ssh-keygen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ed25519&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-email@example.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Press Enter to accept default location&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.3 Copy SSH Key to VPS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;First-time connection:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-copy-id your-username@your-vps-ip
&lt;span class="c"&gt;# Enter the password provided by Infomaniak&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Alternative method (if ssh-copy-id is not available):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Display your public key&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519.pub

&lt;span class="c"&gt;# Then manually add it to your VPS:&lt;/span&gt;
ssh your-username@your-vps-ip
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.ssh
nano ~/.ssh/authorized_keys
&lt;span class="c"&gt;# Paste your public key, save and exit&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 ~/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/authorized_keys
&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.4 Configure VS Code SSH
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In VS Code, press &lt;code&gt;Ctrl+Shift+P&lt;/code&gt; (or &lt;code&gt;Cmd+Shift+P&lt;/code&gt; on macOS)&lt;/li&gt;
&lt;li&gt;Type "Remote-SSH: Open SSH Configuration File"&lt;/li&gt;
&lt;li&gt;Select your SSH config file (usually &lt;code&gt;~/.ssh/config&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Add this configuration:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host infomaniak-vps
    HostName your-vps-ip
    User your-username
    Port 22
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 60
    ServerAliveCountMax 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;your-vps-ip&lt;/code&gt; with your actual VPS IP address&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;your-username&lt;/code&gt; with your VPS username&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.5 Connect to VPS from VS Code
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Press &lt;code&gt;Ctrl+Shift+P&lt;/code&gt; (or &lt;code&gt;Cmd+Shift+P&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Type "Remote-SSH: Connect to Host"&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;infomaniak-vps&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;VS Code will open a new window connected to your VPS&lt;/li&gt;
&lt;li&gt;You can now browse files and use the integrated terminal&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.6 VS Code Tips for Remote Development
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open folder&lt;/strong&gt;: Click "Open Folder" and select &lt;code&gt;~/w3pk-playground&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal&lt;/strong&gt;: Use &lt;code&gt;Ctrl+&lt;/code&gt; to open integrated terminal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensions&lt;/strong&gt;: Install extensions on the remote server (Node.js, ESLint, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port forwarding&lt;/strong&gt;: VS Code automatically forwards ports - useful for testing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Fork the Repository
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://github.com/w3hc/w3pk-playground" rel="noopener noreferrer"&gt;https://github.com/w3hc/w3pk-playground&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Fork&lt;/strong&gt; button in the top right&lt;/li&gt;
&lt;li&gt;Clone your forked repository to your local machine:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/YOUR_USERNAME/w3pk-playground.git
&lt;span class="nb"&gt;cd &lt;/span&gt;w3pk-playground
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. VPS Initial Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Connect to your VPS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Via VS Code (recommended):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the Remote-SSH connection you configured above&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Via terminal:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh your-username@your-vps-ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 Update System
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.3 Install Node.js via NVM
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install NVM&lt;/span&gt;
curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash

&lt;span class="c"&gt;# Reload shell configuration&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc

&lt;span class="c"&gt;# Install Node.js (latest LTS)&lt;/span&gt;
nvm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--lts&lt;/span&gt;

&lt;span class="c"&gt;# Verify installation&lt;/span&gt;
node &lt;span class="nt"&gt;-v&lt;/span&gt;
npm &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.4 Install pnpm
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; pnpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.5 Install PM2 (Process Manager)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add &lt;span class="nt"&gt;-g&lt;/span&gt; pm2

&lt;span class="c"&gt;# Setup PM2 to start on system reboot&lt;/span&gt;
pm2 startup
&lt;span class="c"&gt;# Follow the instructions provided by the command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.6 Clone your repository on the VPS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~
git clone https://github.com/YOUR_USERNAME/w3pk-playground.git
&lt;span class="nb"&gt;cd &lt;/span&gt;w3pk-playground
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.7 Install dependencies and build
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nb"&gt;install
&lt;/span&gt;pnpm build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.8 Start the app with PM2
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 start pnpm &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"your-app-name"&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; start
pm2 save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it's running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 list
pm2 logs your-app-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Domain and DNS Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Configure DNS
&lt;/h3&gt;

&lt;p&gt;In your domain provider's DNS settings (e.g., Infomaniak DNS panel):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add an &lt;strong&gt;A Record&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: &lt;code&gt;subdomain&lt;/code&gt; (e.g., &lt;code&gt;app&lt;/code&gt; for &lt;code&gt;app.yourdomain.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type&lt;/strong&gt;: A&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value&lt;/strong&gt;: Your VPS IP address&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL&lt;/strong&gt;: 300 (or default)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wait for DNS propagation (can take up to 24 hours, usually 5-15 minutes)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  7.2 Verify DNS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig subdomain.yourdomain.com +short
&lt;span class="c"&gt;# Should return your VPS IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Nginx Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1 Install Nginx
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.2 Create Nginx configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/your-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;subdomain.yourdomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;'upgrade'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_cache_bypass&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.3 Enable the site
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create symbolic link&lt;/span&gt;
&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/your-app /etc/nginx/sites-enabled/

&lt;span class="c"&gt;# Test configuration&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;

&lt;span class="c"&gt;# Reload Nginx&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. SSL Certificate Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7.1 Install Certbot
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.2 Obtain SSL certificate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; subdomain.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enter your email address&lt;/li&gt;
&lt;li&gt;Agree to terms of service&lt;/li&gt;
&lt;li&gt;Choose to redirect HTTP to HTTPS (recommended)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7.3 Verify SSL certificate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check certificate status&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot certificates

&lt;span class="c"&gt;# Test auto-renewal&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot renew &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your site should now be accessible at &lt;code&gt;https://subdomain.yourdomain.com&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. UFW Firewall Configuration (Additional Layer)
&lt;/h2&gt;

&lt;p&gt;Configure Ubuntu's UFW firewall for additional security.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install UFW (usually pre-installed)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;ufw &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Allow SSH (IMPORTANT: do this first!)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 22/tcp

&lt;span class="c"&gt;# Allow HTTP and HTTPS&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 80/tcp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 443/tcp

&lt;span class="c"&gt;# Enable UFW&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;

&lt;span class="c"&gt;# Check status&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status verbose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Always allow SSH (port 22) before enabling UFW, or you'll lose access to your VPS!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  9. GitHub Actions CI/CD Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  9.1 Create SSH key for GitHub Actions
&lt;/h3&gt;

&lt;p&gt;On your VPS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate SSH key WITHOUT passphrase (important!)&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"github-actions"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/github-actions &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;

&lt;span class="c"&gt;# Add public key to authorized_keys&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/github-actions.pub &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.ssh/authorized_keys

&lt;span class="c"&gt;# Set correct permissions&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 ~/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/authorized_keys
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/github-actions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.2 Copy the private key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/github-actions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the entire output including &lt;code&gt;-----BEGIN&lt;/code&gt; and &lt;code&gt;-----END&lt;/code&gt; lines.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.3 Create GitHub workflow file
&lt;/h3&gt;

&lt;p&gt;In your local repository, create &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to VPS&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to VPS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@v1.0.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VPS_SSH_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;export NVM_DIR="$HOME/.nvm"&lt;/span&gt;
            &lt;span class="s"&gt;[ -s "$NVM_DIR/nvm.sh" ] &amp;amp;&amp;amp; \. "$NVM_DIR/nvm.sh"&lt;/span&gt;
            &lt;span class="s"&gt;export PATH="$HOME/.local/share/pnpm:$PATH"&lt;/span&gt;
            &lt;span class="s"&gt;cd ~/w3pk-playground&lt;/span&gt;
            &lt;span class="s"&gt;git pull origin main&lt;/span&gt;
            &lt;span class="s"&gt;pnpm install&lt;/span&gt;
            &lt;span class="s"&gt;pnpm build&lt;/span&gt;
            &lt;span class="s"&gt;pm2 restart your-app-name&lt;/span&gt;
            &lt;span class="s"&gt;pm2 save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.4 Add GitHub Secrets
&lt;/h3&gt;

&lt;p&gt;Go to your GitHub repository:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Secrets and variables&lt;/strong&gt; → &lt;strong&gt;Actions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New repository secret&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add three secrets:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;VPS_HOST&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-vps-ip-address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;VPS_USERNAME&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-vps-username
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;VPS_SSH_KEY&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(paste the entire private key from ~/.ssh/github-actions)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.5 Commit and push
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add .github/workflows/deploy.yml
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add CI/CD workflow"&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  10. Test Your Deployment
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Make a change to your code&lt;/li&gt;
&lt;li&gt;Commit and push to the &lt;code&gt;main&lt;/code&gt; branch&lt;/li&gt;
&lt;li&gt;Go to your GitHub repository → &lt;strong&gt;Actions&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Watch the deployment workflow run&lt;/li&gt;
&lt;li&gt;Visit &lt;code&gt;https://subdomain.yourdomain.com&lt;/code&gt; to see your changes&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Useful Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  PM2 Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 list                    &lt;span class="c"&gt;# List all apps&lt;/span&gt;
pm2 logs your-app-name     &lt;span class="c"&gt;# View logs&lt;/span&gt;
pm2 restart your-app-name  &lt;span class="c"&gt;# Restart app&lt;/span&gt;
pm2 stop your-app-name     &lt;span class="c"&gt;# Stop app&lt;/span&gt;
pm2 delete your-app-name   &lt;span class="c"&gt;# Remove app&lt;/span&gt;
pm2 monit                  &lt;span class="c"&gt;# Monitor all apps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Nginx Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;              &lt;span class="c"&gt;# Test configuration&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx &lt;span class="c"&gt;# Reload configuration&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart nginx &lt;span class="c"&gt;# Restart Nginx&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status nginx &lt;span class="c"&gt;# Check status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SSL Certificate Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot certificates  &lt;span class="c"&gt;# List all certificates&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot renew        &lt;span class="c"&gt;# Renew certificates&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot delete       &lt;span class="c"&gt;# Delete a certificate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking Open Ports
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ss &lt;span class="nt"&gt;-tulpn&lt;/span&gt;             &lt;span class="c"&gt;# Show all listening ports&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :3000        &lt;span class="c"&gt;# Check specific port&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Firewall Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Infomaniak Firewall: Manage via Infomaniak Manager web interface&lt;/span&gt;

&lt;span class="c"&gt;# UFW Firewall&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status verbose    &lt;span class="c"&gt;# Check UFW status&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 3000/tcp   &lt;span class="c"&gt;# Allow specific port&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw deny 3000/tcp    &lt;span class="c"&gt;# Deny specific port&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw delete allow 3000/tcp  &lt;span class="c"&gt;# Remove rule&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw reset            &lt;span class="c"&gt;# Reset all rules (careful!)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cannot Connect via SSH
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check Infomaniak firewall&lt;/strong&gt;: Ensure port 22 is allowed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check UFW&lt;/strong&gt;: Run &lt;code&gt;sudo ufw allow 22/tcp&lt;/code&gt; if you have terminal access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Infomaniak Console&lt;/strong&gt;: Access your VPS through the web console in Infomaniak Manager&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify IP&lt;/strong&gt;: Ensure you're using the correct VPS IP address&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  502 Bad Gateway
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Check if your app is running: &lt;code&gt;pm2 list&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check app logs: &lt;code&gt;pm2 logs your-app-name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verify the port in Nginx config matches your app's port&lt;/li&gt;
&lt;li&gt;Restart the app: &lt;code&gt;pm2 restart your-app-name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Build Fails in CI/CD
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Check GitHub Actions logs for specific errors&lt;/li&gt;
&lt;li&gt;Verify SSH key is correct in GitHub secrets&lt;/li&gt;
&lt;li&gt;Ensure NVM and pnpm paths are correct in workflow&lt;/li&gt;
&lt;li&gt;Test commands manually on VPS first&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  SSL Certificate Issues
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Verify DNS is pointing to your VPS: &lt;code&gt;dig subdomain.yourdomain.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check if port 80 and 443 are open in both Infomaniak firewall and UFW&lt;/li&gt;
&lt;li&gt;Review Certbot logs: &lt;code&gt;sudo cat /var/log/letsencrypt/letsencrypt.log&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Permission Denied Errors
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Fix SSH permissions&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 ~/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/authorized_keys
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/github-actions

&lt;span class="c"&gt;# Fix file ownership&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; your-username:your-username ~/w3pk-playground
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Port Already in Use
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check what's using the port&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :3000

&lt;span class="c"&gt;# Kill the process if needed&lt;/span&gt;
&lt;span class="nb"&gt;sudo kill&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt; PID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Two-Layer Firewall&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Infomaniak firewall (network level)&lt;/li&gt;
&lt;li&gt;UFW firewall (system level)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSH Security&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/ssh/sshd_config
   &lt;span class="c"&gt;# Set: PasswordAuthentication no&lt;/span&gt;
   &lt;span class="c"&gt;# Set: PermitRootLogin no&lt;/span&gt;
   &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Regular Updates&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fail2Ban&lt;/strong&gt; (optional but recommended):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;fail2ban &lt;span class="nt"&gt;-y&lt;/span&gt;
   &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;fail2ban
   &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start fail2ban
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Backups&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Infomaniak's backup service&lt;/li&gt;
&lt;li&gt;Regularly backup your application and database&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Monitoring&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check Infomaniak Manager for resource usage&lt;/li&gt;
&lt;li&gt;Monitor PM2 logs regularly&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Infomaniak-Specific Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  VPS Snapshots
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to Infomaniak Manager → Your VPS&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Snapshots&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Create snapshots before major changes&lt;/li&gt;
&lt;li&gt;Restore from snapshots if needed&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Resource Monitoring
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;View CPU, RAM, and disk usage in Infomaniak Manager&lt;/li&gt;
&lt;li&gt;Set up alerts for resource thresholds&lt;/li&gt;
&lt;li&gt;Upgrade your VPS plan if needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Infomaniak provides 24/7 support&lt;/li&gt;
&lt;li&gt;Access support through the Manager interface&lt;/li&gt;
&lt;li&gt;Community forum: &lt;a href="https://community.infomaniak.com" rel="noopener noreferrer"&gt;community.infomaniak.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Set up monitoring with tools like UptimeRobot&lt;/li&gt;
&lt;li&gt;Configure database backups&lt;/li&gt;
&lt;li&gt;Add staging environment&lt;/li&gt;
&lt;li&gt;Implement health checks in your workflow&lt;/li&gt;
&lt;li&gt;Set up logging with a service like Logtail or Papertrail&lt;/li&gt;
&lt;li&gt;Consider using Infomaniak's CDN for static assets&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cost Estimation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;VPS Start Plan (3.60 € / month):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suitable for: 1-3 small Next.js apps&lt;/li&gt;
&lt;li&gt;Traffic: Up to moderate traffic levels&lt;/li&gt;
&lt;li&gt;Storage: 20 GB (sufficient for code and small databases)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to upgrade:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High traffic (&amp;gt; 10,000 monthly visitors per app)&lt;/li&gt;
&lt;li&gt;Resource-intensive applications&lt;/li&gt;
&lt;li&gt;Multiple production applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.infomaniak.com/en/support/faq" rel="noopener noreferrer"&gt;Infomaniak VPS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs" rel="noopener noreferrer"&gt;Next.js Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pm2.keymetrics.io/" rel="noopener noreferrer"&gt;PM2 Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nginx.org/en/docs/" rel="noopener noreferrer"&gt;Nginx Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://letsencrypt.org/docs/" rel="noopener noreferrer"&gt;Let's Encrypt Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Congratulations!&lt;/strong&gt; 🎉 Your Next.js app is now deployed on Infomaniak VPS with automatic CI/CD!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>vp</category>
      <category>cicd</category>
    </item>
    <item>
      <title>How to deploy to Arthera Mainnet</title>
      <dc:creator>Julien Béranger</dc:creator>
      <pubDate>Fri, 01 Mar 2024 17:07:04 +0000</pubDate>
      <link>https://dev.to/julienbrg/how-to-deploy-to-arthera-mainnet-110f</link>
      <guid>https://dev.to/julienbrg/how-to-deploy-to-arthera-mainnet-110f</guid>
      <description>&lt;p&gt;Arthera Mainnet is &lt;a href="https://explorer.arthera.net" rel="noopener noreferrer"&gt;live&lt;/a&gt; and it’s time to deploy!&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll learn how to deploy your Solidity contracts to Arthera. We’ll first deploy to the Testnet, and then deploy to Arthera Mainnet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy to Testnet
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Go to the &lt;a href="https://faucet-test.arthera.net/" rel="noopener noreferrer"&gt;subscription faucet&lt;/a&gt; (Testnet), paste your wallet address there: you will get a subscription NFT, it is needed since there’s no AA (native currency of Arthera) in circulation on the network yet. You will deploy your contracts using this address &lt;strong&gt;even if the balance is zero&lt;/strong&gt;, that’s part of the magic of Arthera.&lt;/li&gt;
&lt;li&gt;Deploy your Solidity contracts using your favourite tool (Hardhat, Foundry,...) and you can check if everything went well on the &lt;a href="https://explorer-test.arthera.net/" rel="noopener noreferrer"&gt;explorer&lt;/a&gt; (Testnet). Sourcify supports Arthera, so you can verify your contracts both manually and programmatically (you can learn more about this in our docs).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, when I deployed a simple NFT contract (&lt;a href="https://github.com/artheranet/whitepaper-nft-contracts" rel="noopener noreferrer"&gt;view the source code here&lt;/a&gt;), the output looked like this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6e3242ehye3y4qd53i78.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6e3242ehye3y4qd53i78.png" alt="output screenshot" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Optional steps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If you want to offer your users a gas-less experience, you can add these few lines of code in your contract following &lt;a href="https://docs.arthera.net/build/tutorials/use-subscriptions" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Configure the (Testnet) SMP (Subscription Management Platform): &lt;a href="https://smp-test.arthera.net/" rel="noopener noreferrer"&gt;https://smp-test.arthera.net/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add the checkout links to your app or website.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploy to Mainnet
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Go to the &lt;a href="https://faucet.arthera.net/" rel="noopener noreferrer"&gt;subscription faucet&lt;/a&gt; (Mainnet) to get your subscription NFT. For more complex deployments, feel free to reach out to the team. &lt;/li&gt;
&lt;li&gt;Deploy your contracts from this address. It should be visible on the &lt;a href="https://explorer-test.arthera.net/" rel="noopener noreferrer"&gt;explorer&lt;/a&gt; (Mainnet)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optional steps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If you want to offer your users a gas-less experience, you can add these few lines of code in your contract following &lt;a href="https://docs.arthera.net/build/tutorials/use-subscriptions" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Configure the (Mainnet) SMP (Subscription Management Platform): &lt;a href="https://smp.arthera.net/" rel="noopener noreferrer"&gt;https://smp.arthera.net/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add the checkout links to your app or website.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Congrats: &lt;strong&gt;you're on mainnet&lt;/strong&gt;! 🎉&lt;/p&gt;




&lt;p&gt;The full dev documentation for Arthera is here: &lt;strong&gt;&lt;a href="https://docs.arthera.net/" rel="noopener noreferrer"&gt;https://docs.arthera.net/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And if you need any help or have questions, feel free to contact me directly via &lt;a href="https://matrix.to/#/@julienbrg:matrix.org" rel="noopener noreferrer"&gt;Element&lt;/a&gt;, &lt;a href="https://t.me/julienbrg" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt;, &lt;a href="https://twitter.com/julienbrg" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://discord.com/users/julienbrg" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/julienberanger/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>solidity</category>
      <category>web3</category>
      <category>blockchain</category>
      <category>arthera</category>
    </item>
    <item>
      <title>How to handle your Arthera Lifetime Subscription NFTs</title>
      <dc:creator>Julien Béranger</dc:creator>
      <pubDate>Tue, 21 Nov 2023 16:31:59 +0000</pubDate>
      <link>https://dev.to/julienbrg/how-to-handle-your-arthera-lifetime-subscription-nfts-db9</link>
      <guid>https://dev.to/julienbrg/how-to-handle-your-arthera-lifetime-subscription-nfts-db9</guid>
      <description>&lt;p&gt;&lt;a href="https://www.arthera.net/" rel="noopener noreferrer"&gt;Arthera&lt;/a&gt; is a Layer 1 EVM-compatible blockchain, improved by DAG-based technology. Testnet is live and Mainnet launches on December 25th 2023.&lt;/p&gt;

&lt;p&gt;With a dual model offering both pay-as-you-go and subscription options, Arthera's Lifetime Subscriptions (LTS) provide unparalleled access to our evolving ecosystem. On Arthera you subscribe and &lt;strong&gt;forget gas fees - for life&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get yours &lt;a href="https://lifetime.arthera.net/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/strong&gt;, and/or create your own referral link &lt;a href="https://lifetime.arthera.net/" rel="noopener noreferrer"&gt;there&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can get your limited edition of Arthera Lifetime Subscription (LTS) on seven different networks: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arbitrum (222)&lt;/li&gt;
&lt;li&gt;Avalanche (111)&lt;/li&gt;
&lt;li&gt;Base (111)&lt;/li&gt;
&lt;li&gt;BNB Chain (333)&lt;/li&gt;
&lt;li&gt;Ethererum (444)&lt;/li&gt;
&lt;li&gt;Optimism (111)&lt;/li&gt;
&lt;li&gt;Polygon (333)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to add your NFT to your wallet?
&lt;/h2&gt;

&lt;p&gt;The easiest way to add your NFT to your wallet is to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the explorer of the network/blockchain you selected&lt;/li&gt;
&lt;li&gt;Paste your wallet address in the search bar&lt;/li&gt;
&lt;li&gt;Find your NFT, in most of explorers there's an "NFT transfers" or "ERC721 Token Txns"&lt;/li&gt;
&lt;li&gt;Copy-paste the NFT contract address, and keep the NFT ID in mind&lt;/li&gt;
&lt;li&gt;Open MetaMask, and click on the "NFTs" tab&lt;/li&gt;
&lt;li&gt;Click on "Import NFT"&lt;/li&gt;
&lt;li&gt;Paste the NFT contract address and NFT ID&lt;/li&gt;
&lt;li&gt;Click on "confirm"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Voilà! 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcebk3l20m94pv6oe9fgo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcebk3l20m94pv6oe9fgo.png" alt="julien-LTS-NFT" width="714" height="1206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now transfer your NFT using MetaMask wallet. &lt;/p&gt;

&lt;p&gt;If you're using any other wallet, there should be some way to add it to your wallet.&lt;/p&gt;

&lt;h2&gt;
  
  
  View your NFT
&lt;/h2&gt;

&lt;p&gt;You can also view your NFT in a lot of different NFT marketplaces depending on which network you have claimed it. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;In a near future, you'll be able to use this NFT to activate your lifetime subscription, forget gas fees, and make up to 30 daily transactions on Arthera at no cost at all. For Life. &lt;/p&gt;

</description>
      <category>arthera</category>
      <category>nfts</category>
      <category>subscription</category>
    </item>
    <item>
      <title>Get started with Arthera</title>
      <dc:creator>Julien Béranger</dc:creator>
      <pubDate>Tue, 17 Oct 2023 07:49:04 +0000</pubDate>
      <link>https://dev.to/julienbrg/get-started-with-arthera-3fg5</link>
      <guid>https://dev.to/julienbrg/get-started-with-arthera-3fg5</guid>
      <description>&lt;p&gt;&lt;a href="https://www.arthera.net/" rel="noopener noreferrer"&gt;Arthera&lt;/a&gt; is a new L1 in town. It's EVM-compatible, so if you're already familiar with EVM tooling and Solidity, you'll feel right X I'm at home.&lt;/p&gt;

&lt;p&gt;We just sealed a partnership with a service called &lt;a href="https://solidityscan.com/home" rel="noopener noreferrer"&gt;SolidityScan&lt;/a&gt;, and it's part of the whole security workflow we're setting up for our own system contracts. But we're also giving access to all applications building on Arthera. To date, there are already more than 80 apps committed to building on Arthera.&lt;/p&gt;

&lt;p&gt;I strongly recommend you have a look at our White Paper and &lt;a href="https://whitepaper.arthera.net/" rel="noopener noreferrer"&gt;mint the NFT&lt;/a&gt; that goes with it.&lt;/p&gt;

&lt;p&gt;If you're as lazy (efficient) as a good dev should be, or a beginner in Solidity, you can just fork this &lt;a href="https://github.com/artheranet/arthera-hardhat-template" rel="noopener noreferrer"&gt;Hardhat template&lt;/a&gt; and build what you want to build from there.&lt;/p&gt;

&lt;p&gt;Here are the &lt;a href="https://docs.arthera.net/" rel="noopener noreferrer"&gt;docs&lt;/a&gt; where you'll find answers to all your questions. Whether you're using Hardhat, Foundry, Truffle, Alchemy, Embark, or something else, you can easily add Arthera Testnet to your project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;RPC Endpoint: &lt;strong&gt;&lt;a href="https://rpc-test.arthera.net" rel="noopener noreferrer"&gt;https://rpc-test.arthera.net&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebSocket Endpoint (Optional): &lt;strong&gt;wss://ws-test.arthera.net&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chain ID: &lt;strong&gt;10243&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And of course, you're invited to &lt;a href="https://t.me/artherachain" rel="noopener noreferrer"&gt;join us in Telegram&lt;/a&gt; and get in touch with us!&lt;/p&gt;

&lt;p&gt;This is our &lt;a href="https://store.arthera.net/" rel="noopener noreferrer"&gt;app store&lt;/a&gt;, feel free to submit your app &lt;a href="https://docs.google.com/forms/d/e/1FAIpQLSdEzTThOjuQwYPfS8L9Hrf3boxumolQP7lo4hySpbQPasVFsg/viewform" rel="noopener noreferrer"&gt;here&lt;/a&gt;, we'll be delighted to welcome you.&lt;/p&gt;

</description>
      <category>arthera</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>How to add Arthera subscriptions to your Solidity contracts</title>
      <dc:creator>Julien Béranger</dc:creator>
      <pubDate>Wed, 11 Oct 2023 14:12:15 +0000</pubDate>
      <link>https://dev.to/julienbrg/how-to-add-arthera-subscriptions-to-your-solidity-contracts-43do</link>
      <guid>https://dev.to/julienbrg/how-to-add-arthera-subscriptions-to-your-solidity-contracts-43do</guid>
      <description>&lt;p&gt;Forcing users to buy a handful of ETH before they can interact with your contract often makes it unpleasant for them to use your service. It's a classic issue in the crypto space and we all know this kind of friction can make your beloved users leave, never to return again.&lt;/p&gt;

&lt;p&gt;Teams like &lt;a href="https://www.biconomy.io/" rel="noopener noreferrer"&gt;Biconomy&lt;/a&gt;, &lt;a href="https://www.gelato.network/" rel="noopener noreferrer"&gt;Gelato&lt;/a&gt; and others have been working hard to create solutions that remove this friction, mainly by allowing your app to sponsor the transaction fees.&lt;/p&gt;

&lt;p&gt;On &lt;a href="https://bitpushnews.medium.com/eip-4337-will-account-abstraction-bring-mass-adoption-to-ethereum-2293c1a28137" rel="noopener noreferrer"&gt;March 1st, 2023&lt;/a&gt;, the Ethereum community collectively &lt;a href="https://eips.ethereum.org/EIPS/eip-4337" rel="noopener noreferrer"&gt;decided&lt;/a&gt; to natively support what we now call &lt;em&gt;smart wallets&lt;/em&gt; or &lt;em&gt;on-chain wallets&lt;/em&gt;, referring to account abstraction implementations. &lt;a href="https://etherspot.io/" rel="noopener noreferrer"&gt;Etherspot&lt;/a&gt;, &lt;a href="https://docs.cometh.io/connect/quickstart/what-is-connect" rel="noopener noreferrer"&gt;Cometh Connect&lt;/a&gt; or &lt;a href="https://safe.global/core" rel="noopener noreferrer"&gt;Safe&lt;/a&gt;, and several others are making it easier for devs to integrate with account abstraction to improve the UX for their own app. At &lt;a href="https://docs.arthera.net/build/intro" rel="noopener noreferrer"&gt;Arthera&lt;/a&gt; we will hopefully work with all of them. However, as Fabian Vogelsteller mentioned in a &lt;a href="https://twitter.com/lukso_io/status/1685255791777869825" rel="noopener noreferrer"&gt;recent interview&lt;/a&gt;, account abstraction can be pretty complex.&lt;/p&gt;

&lt;p&gt;Arthera's approach to fix the &lt;em&gt;classical Web3 UX issue&lt;/em&gt; is pretty straight forward. If you want to pay for your gas fees you can: we call it "pay-as-you-go" and it can be useful in some cases. But one of the key features of Arthera is the built-in subscription model where, as an individual, you can purchase a $1 USD monthly subscription and then just &lt;strong&gt;forget about gas fees&lt;/strong&gt;. Moreover, As an app provider, you can present a gas-less service to your users and that's the part I will cover in this tutorial.&lt;/p&gt;

&lt;p&gt;Enough talking for today... let’s build!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz36km0w64bx4l49uy05h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz36km0w64bx4l49uy05h.png" alt=" " width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;When you get a subscription plan for your app, you can then whitelist your users so they can use your service even if they have zero AA (the native currency of Arthera) in their wallet.&lt;/p&gt;

&lt;p&gt;In addition to that, the SMP (&lt;a href="https://smp-dev.arthera.net/" rel="noopener noreferrer"&gt;Subscription Management Platform&lt;/a&gt;) will allow you to customise certain things, and also &lt;strong&gt;create a subscription widget that supports fiat payments&lt;/strong&gt;. This widget can be easily integrated into your app. To my knowledge this is a world first. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://wallet-test.arthera.net/" rel="noopener noreferrer"&gt;Arthera Wallet UI v1&lt;/a&gt; and export the private key to your smart contract development environment (Hardhat, Foundry, etc).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2
&lt;/h3&gt;

&lt;p&gt;Add the &lt;code&gt;ISubscriptionOwner.sol&lt;/code&gt; contract interface to your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: GPL-3.0&lt;/span&gt;
&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ISubscriptionOwner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getSubscriptionOwner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: you can also directly add this contract in your contract file.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3
&lt;/h3&gt;

&lt;p&gt;Add the following imports at the top of your Solidity contract file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ISubscriptionOwner.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@openzeppelin/contracts/utils/introspection/ERC165.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4
&lt;/h3&gt;

&lt;p&gt;Make your contract inherit from both &lt;code&gt;ISubscriptionOwner&lt;/code&gt; and &lt;code&gt;ERC165&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;YourContract&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;ISubscriptionOwner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ERC165&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5
&lt;/h3&gt;

&lt;p&gt;Add the &lt;code&gt;getSubscriptionOwner()&lt;/code&gt; and &lt;code&gt;supportsInterface()&lt;/code&gt; functions at the end of your contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getSubscriptionOwner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ADDRESS_OF_THE_SUBSCRIPTION_OWNER&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;supportsInterface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes4&lt;/span&gt; &lt;span class="nx"&gt;interfaceId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nf"&gt;override&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ERC165&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;interfaceId&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ISubscriptionOwner&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;interfaceId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;supportsInterface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interfaceId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The address &lt;code&gt;&amp;lt;ADDRESS_OF_THE_SUBSCRIPTION_OWNER&amp;gt;&lt;/code&gt; must be the account you created in the beginning of this tutorial (step 1).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ISubscriptionOwner&lt;/code&gt; interface allows Arthera to determine the owner of the subscription, which must be an EOA account (it can't be a contract).&lt;/p&gt;

&lt;p&gt;If your contract is ownable, your &lt;code&gt;getSubscriptionOwner()&lt;/code&gt; function would eventually look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getSubscriptionOwner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your contracts inherit from other contracts, you must ensure the order of inheritances is correct; keep in mind that the ERC165 often goes first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6
&lt;/h3&gt;

&lt;p&gt;Deploy your contract to Arthera Testnet. &lt;/p&gt;

&lt;p&gt;Here is the network info: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RPC Endpoint: &lt;strong&gt;&lt;a href="https://rpc-test.arthera.net" rel="noopener noreferrer"&gt;https://rpc-test.arthera.net&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Chain ID: &lt;strong&gt;10243&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 7
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Go to the Arthera &lt;a href="https://smp-test.arthera.net" rel="noopener noreferrer"&gt;SMP&lt;/a&gt; (Subscription Management Platform)&lt;/li&gt;
&lt;li&gt;In the left menu, click on the "On-Chain" tab&lt;/li&gt;
&lt;li&gt;Connect with the subscription owner wallet&lt;/li&gt;
&lt;li&gt;Click on "Subscribe" (&lt;strong&gt;DApp Plan&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Paste your contract address in the field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should always use the same account, it's what we call the &lt;code&gt;subscriptionOwner&lt;/code&gt;. Get a dApp subscription, then paste the address of your newly deployed contract in the field.&lt;/p&gt;

&lt;p&gt;Done! 🎉&lt;/p&gt;

&lt;p&gt;At this stage, you're ready to whitelist your users so they can interact with your app without paying any gas fees.&lt;/p&gt;

&lt;p&gt;Welcome to Arthera: Enjoy Web3, forget gas fees.&lt;/p&gt;




&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://t.me/artherachain/8" rel="noopener noreferrer"&gt;Tech support TG group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.arthera.net/build/intro" rel="noopener noreferrer"&gt;Arthera documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.arthera.net/build/intro" rel="noopener noreferrer"&gt;Integration recommendations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.arthera.net/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://store.arthera.net/" rel="noopener noreferrer"&gt;App Store&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://whitepaper.arthera.net/" rel="noopener noreferrer"&gt;Arthera White Paper NFT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>solidity</category>
      <category>arthera</category>
      <category>dsaas</category>
    </item>
    <item>
      <title>Deploy an ERC-20 on Aurora using Hardhat</title>
      <dc:creator>Julien Béranger</dc:creator>
      <pubDate>Tue, 08 Nov 2022 16:38:52 +0000</pubDate>
      <link>https://dev.to/julienbrg/deploy-an-erc-20-on-aurora-using-hardhat-4ldh</link>
      <guid>https://dev.to/julienbrg/deploy-an-erc-20-on-aurora-using-hardhat-4ldh</guid>
      <description>&lt;p&gt;&lt;a href="https://aurora.dev/" rel="noopener noreferrer"&gt;Aurora&lt;/a&gt; is an EVM-based network that benefits from the security and scalability of NEAR. Technically, Aurora is a shard of NEAR that supports &lt;a href="https://soliditylang.org/" rel="noopener noreferrer"&gt;Solidity&lt;/a&gt;. There's nothing as simple as deploying an ERC-20 token on Aurora Testnet. &lt;/p&gt;

&lt;h2&gt;
  
  
  Aurora Testnet setup
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://aurora.dev/start" rel="noopener noreferrer"&gt;https://aurora.dev/start&lt;/a&gt;, in the &lt;code&gt;Aurora Network&lt;/code&gt; section, select the &lt;code&gt;testnet&lt;/code&gt; tab and click on &lt;code&gt;Add Network&lt;/code&gt;: this will add Aurora Testnet as a new network in your MetaMask wallet.&lt;/p&gt;

&lt;p&gt;Head to &lt;a href="https://aurora.dev/faucet" rel="noopener noreferrer"&gt;Aurora Testnet faucet&lt;/a&gt; and click on the &lt;code&gt;testnet&lt;/code&gt; button. Now you can just click on the &lt;code&gt;Connect to Aurora Testnet&lt;/code&gt; button, it will turn into a &lt;code&gt;Request 0.001 ETH from faucet&lt;/code&gt; button: just click it to get 0.001 ETH. That will be more than enough to deploy your ERC-20 token contract.&lt;/p&gt;

&lt;p&gt;If you need more, you can use the &lt;a href="https://testnet.rainbowbridge.app/" rel="noopener noreferrer"&gt;Testnet Rainbow Bridge&lt;/a&gt; to transfer some Goerli ETH to Aurora Testnet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardhat config
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Clone this repository: &lt;a href="https://github.com/julienbrg/dune-erc20" rel="noopener noreferrer"&gt;https://github.com/julienbrg/dune-erc20&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Copy the &lt;code&gt;.env.example&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Rename it &lt;code&gt;.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In MetaMask, export the private key of the wallet holding the Aurora Testnet ETH, copy-paste it after the equal sign (line 3)&lt;/li&gt;
&lt;li&gt;In your terminal (in your root directory):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The ERC-20 contract
&lt;/h2&gt;

&lt;p&gt;This is a very basic ERC-20 token that's using open Zeppelin Solidity contracts library. There's only one function: the &lt;code&gt;mint()&lt;/code&gt; function. The visibility is set to public, meaning that anyone can call this function and mint as many units as he/she wants (we're in an 'open bar' mode, so to speak):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Dune is ERC20 {
    constructor() ERC20("Dune", "DUNE") {}

    function mint(address to, uint256 amount) public {
        _mint(to, amount);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test this contract with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat test 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then just deploy it to Aurora Testnet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat run scripts/deploy.ts --network aurora_testnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERC-20 deployed at 0x51723295B6E50d94D887Bbc70c1AE0ab1Fa3a469 ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On &lt;a href="https://testnet.aurorascan.dev/tx/0x375eaf92f4b4629968e5e3c622a96c00da0d610edf380e5e87d5ea8f98a94e52" rel="noopener noreferrer"&gt;Aurorascan&lt;/a&gt;, your deployment transaction will look like this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fer6hpx7cknok1sj6lpa6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fer6hpx7cknok1sj6lpa6.png" alt="Aurorascan tx" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congrats, you just deployed a Solidity contract to Aurora Testnet! 🎉&lt;/p&gt;

&lt;p&gt;To mint and transfer a handful of DUNE to yourself (or to a friend of yours), you can use this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat run scripts/transfer.ts --network aurora_testnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For info, there's even a frontend available if you want to mint some Dune tokens: &lt;a href="https://dune-aurora.netlify.app/" rel="noopener noreferrer"&gt;https://dune-aurora.netlify.app/&lt;/a&gt;. It was made using React, &lt;a href="https://web3auth.io/" rel="noopener noreferrer"&gt;Web3Auth&lt;/a&gt; and a builder called &lt;a href="https://www.plasmic.app/" rel="noopener noreferrer"&gt;Plasmic&lt;/a&gt;. The Github repo is &lt;a href="https://github.com/julienbrg/my-little-central-bank/tree/dune" rel="noopener noreferrer"&gt;here&lt;/a&gt; (there's a &lt;code&gt;dune&lt;/code&gt; branch) if you want to have a look.&lt;/p&gt;

&lt;p&gt;Now let's deploy to Mainnet... &lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy to Aurora Mainnet
&lt;/h2&gt;

&lt;p&gt;We're going to use a tool called Aurora+. Please head to &lt;a href="https://aurora.plus" rel="noopener noreferrer"&gt;https://aurora.plus&lt;/a&gt;, and sign up to get your 50 free tx per months. Once you've done that, you have your personalized RPC endpoint URL that is waiting for you (top-right menu):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0ztre8yxynm0l1yv2gm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0ztre8yxynm0l1yv2gm.png" alt="personalized endpoint" width="800" height="775"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can copy-paste that URL into your &lt;code&gt;.env&lt;/code&gt; file (line 1).&lt;/p&gt;

&lt;p&gt;Then you want to export your wallet's private key into your &lt;code&gt;.env&lt;/code&gt; file as well.&lt;/p&gt;

&lt;p&gt;Something is missing in that &lt;code&gt;.env&lt;/code&gt; file and it's the Aurorascan API key. You're going to need it to make it easy for everyone to read the source code of your contract, you can use Aurorascan API: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://aurorascan.dev/myapikey" rel="noopener noreferrer"&gt;https://aurorascan.dev/myapikey&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can sign up there, click on API-key in the left menu, then click on &lt;code&gt;add&lt;/code&gt; to create an API key. Copy-paste it in the &lt;code&gt;.env&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;deploy.ts&lt;/code&gt; file uncomment the lines 15, 16 and 17, and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat run scripts/deploy.ts --network aurora_mainnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congrats! 🎉&lt;/p&gt;

&lt;p&gt;Your ERC-20 is deployed to Aurora Mainnet, you paid nothing, the source code is available for everyone to verify what's going on, and people can even trigger the mint function directly from &lt;a href="https://aurorascan.dev/address/0x1692787eF1044d9De9c1b60886891C5b7e33a7C4#code" rel="noopener noreferrer"&gt;Aurorascan&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7igbuygk5s5o0n45nvkx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7igbuygk5s5o0n45nvkx.png" alt="aurorascan" width="799" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you can also use the &lt;a href="https://github.com/julienbrg/dune-erc20/blob/main/scripts/transfer.ts" rel="noopener noreferrer"&gt;&lt;code&gt;transfer.ts&lt;/code&gt;&lt;/a&gt; script to mint some DUNE:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx hardhat run scripts/transfer.ts --network aurora_mainnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks for your read!&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@heitmannj?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Henrik Heitmann&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Feel free to contact me via &lt;a href="https://matrix.to/#/@julienbrg:matrix.org" rel="noopener noreferrer"&gt;Element&lt;/a&gt;, &lt;a href="https://t.me/julienbrg" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt;, &lt;a href="https://twitter.com/julienbrg" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://discord.gg/pfkJpEb4xn" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/julienberanger/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aurora</category>
      <category>hardhat</category>
      <category>tutorial</category>
      <category>solidity</category>
    </item>
    <item>
      <title>Mint an NFT on zkSync 2.0</title>
      <dc:creator>Julien Béranger</dc:creator>
      <pubDate>Sun, 10 Jul 2022 09:41:57 +0000</pubDate>
      <link>https://dev.to/julienbrg/mint-an-nft-on-zksync-20-4bi0</link>
      <guid>https://dev.to/julienbrg/mint-an-nft-on-zksync-20-4bi0</guid>
      <description>&lt;p&gt;As we now live in a &lt;a href="https://www.youtube.com/watch?v=XoQUc3rnKPM" rel="noopener noreferrer"&gt;rollup-centric world&lt;/a&gt;, minting an NFT on  Ethereum Mainnet (L1) is getting less and less relevant. &lt;a href="https://blog.matter-labs.io/zksync-2-0-public-testnet-is-live-de870ba9632a" rel="noopener noreferrer"&gt;zkSync 2.0&lt;/a&gt; public testnet went live in February 2022, so let's just give it a try! &lt;/p&gt;

&lt;p&gt;Please note that zkSync 2.0 &lt;em&gt;Mainnet&lt;/em&gt; is not available yet, but the whole community is waiting for the announcement. When it's out, you'll be ready to deploy your NFT contract. &lt;/p&gt;

&lt;p&gt;We'll work with this Github repository, you can go ahead and clone or fork it: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/julienbrg/zksync-minter" rel="noopener noreferrer"&gt;https://github.com/julienbrg/zksync-minter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I mainly used &lt;a href="https://v2-docs.zksync.io/api/hardhat/getting-started.html" rel="noopener noreferrer"&gt;zkSync 2.0 quickstart&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Please make sure you have the two following tools in place: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://classic.yarnpkg.com/lang/en/docs/install" rel="noopener noreferrer"&gt;Yarn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/get-started/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Add your own keys
&lt;/h2&gt;

&lt;p&gt;Rename the &lt;code&gt;.env.example&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;. To run the app, you need these three keys to be valid. &lt;/p&gt;

&lt;h3&gt;
  
  
  Web3 Storage
&lt;/h3&gt;

&lt;p&gt;As you might know, Web3 Storage uses IPFS and Filecoin. We'll use that to store the metadata of our NFT in a rather persistent way. &lt;/p&gt;

&lt;h3&gt;
  
  
  Infura
&lt;/h3&gt;

&lt;p&gt;You can signup and get a project ID directly on Infura's website: &lt;a href="https://infura.io/" rel="noopener noreferrer"&gt;https://infura.io/&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Wallet private key
&lt;/h3&gt;

&lt;p&gt;From your ArgentX browser wallet, you can export one of your accounts' private key. Make sure you have a handful of Goerli ETH on this wallet. &lt;/p&gt;

&lt;p&gt;Now you can&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Edit the NFT smart contract
&lt;/h2&gt;

&lt;p&gt;In case if your new, an NFT is just a unique digital object. Wether it's a gaming asset, a ticket to a party or an artwork, it most often can be transferred, that's the main feature of an NFT.&lt;/p&gt;

&lt;p&gt;I used the &lt;a href="https://wizard.openzeppelin.com/#erc721" rel="noopener noreferrer"&gt;Contracts Wizard&lt;/a&gt; provided by the all mighty OpenZeppelin team.&lt;/p&gt;

&lt;p&gt;It's a very basic ERC-721 contract. In the constructor, you attach the NFT metadata, mint the ID 1, transfer it to yourself and then revoke the ownership of the contract so that absolutely nobody can mint another NFT with this contract (including yourself).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract Thistle is ERC721, ERC721URIStorage, ERC721Burnable, Ownable {

    constructor(string memory uri) ERC721("Thistle", "THISTLE") {
        safeMint(msg.sender, 1, uri);
        renounceOwnership();
    }

    function _baseURI() internal pure override returns (string memory) {
        return "https://ipfs.io/ipfs/";
    }

    function safeMint(
        address to,
        uint256 tokenId,
        string memory uri
    ) public onlyOwner {
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    function _burn(uint256 tokenId)
        internal
        override(ERC721, ERC721URIStorage)
    {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can change the name of the contract (&lt;code&gt;Thistle&lt;/code&gt;), and also the name and symbol of the NFT in &lt;code&gt;ERC721("Thistle", "THISTLE")&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Once you've done that, your contract is ready. &lt;/p&gt;

&lt;p&gt;You can:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn hardhat compile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Edit the NFT metadata
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;nft&lt;/code&gt; directory, open the &lt;code&gt;metadata.json&lt;/code&gt; file and edit this file exactly like you want. You can use &lt;a href="https://web3.storage/account/" rel="noopener noreferrer"&gt;Web3.Storage&lt;/a&gt; to store an image and paste the url as the value of &lt;code&gt;image&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn hardhat deploy-zksync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get something like this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3u8kdgxplij7hb7s6g38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3u8kdgxplij7hb7s6g38.png" alt="deployment" width="799" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can copy the address of your contract and paste in the search bar of the &lt;a href="https://zksync2-testnet.zkscan.io/" rel="noopener noreferrer"&gt;zkSync 2.0 explorer&lt;/a&gt;. Here's my deployment transaction: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://zksync2-testnet.zkscan.io/tx/0x567037335a70bb01c02bb5f5f6ee2a777f37fc3a3b0c13bae62165e1dbc24c92/token-transfers" rel="noopener noreferrer"&gt;https://zksync2-testnet.zkscan.io/tx/0x567037335a70bb01c02bb5f5f6ee2a777f37fc3a3b0c13bae62165e1dbc24c92/token-transfers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2q2uzk9xyjsprhiccfon.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2q2uzk9xyjsprhiccfon.png" alt="tx screenshot" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You have deployed your NFT contract to zkSync 2.0 testnet and minted 1 NFT! 🎉&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://ato.network/ZkSync/0xccA0521D453cA8C84Cb0b56936A4B764992b2F12/1" rel="noopener noreferrer"&gt;View this NFT on Āto&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You now hold this NFT in your wallet. The tx was confirmed within 3 seconds. If we were on Mainnet it would have costed less than 0.02 USD equivalent.&lt;/p&gt;

&lt;p&gt;When the time's right, you'll be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;move your NFT to Ethereum Mainnet (L1)&lt;/li&gt;
&lt;li&gt;move it to another L2&lt;/li&gt;
&lt;li&gt;build an interface to sell it using the &lt;a href="https://v2-docs.zksync.io/dev/guide/hello-world.html" rel="noopener noreferrer"&gt;zkSync development toolbox&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;add a personalized IP license using &lt;a href="https://ato.network/license" rel="noopener noreferrer"&gt;Āto&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;store it safely using &lt;a href="https://www.argent.xyz/" rel="noopener noreferrer"&gt;Argent&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have any question, feel free to &lt;a href="https://meetings-eu1.hubspot.com/julien-beranger" rel="noopener noreferrer"&gt;call me&lt;/a&gt; or &lt;a href="https://github.com/julienbrg/zksync-minter/issues" rel="noopener noreferrer"&gt;file an issue&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Thanks for reading! &lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@jaymantri?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Jay Mantri&lt;/a&gt;&lt;/p&gt;

</description>
      <category>zksync</category>
      <category>nft</category>
      <category>hardhat</category>
      <category>ethereum</category>
    </item>
  </channel>
</rss>
