diff --git a/dudeswave/Makefile b/dudeswave/Makefile
new file mode 100644
index 0000000..160022d
--- /dev/null
+++ b/dudeswave/Makefile
@@ -0,0 +1,7 @@
+.PHONY: all clean
+
+all:
+ ${MAKE} -C src
+
+clean:
+ ${MAKE} -C src clean
diff --git a/dudeswave/ebin/dudeswave.app b/dudeswave/ebin/dudeswave.app
new file mode 100644
index 0000000..ce137e4
--- /dev/null
+++ b/dudeswave/ebin/dudeswave.app
@@ -0,0 +1,12 @@
+{application,dudeswave,
+ [{description,"The dudeswave web experience"},
+ {vsn,"1.0.0"},
+ {modules,[dudeswave,dudeswave_app,dudeswave_handler,dudeswave_supervisor]},
+ {registered,[]},
+ {applications,[kernel,stdlib,erts,cowboy,ranch]},
+ {mod,{dudeswave_app,[]}},
+ {env, [
+ {ip,"127.0.0.1"},
+ {port,8080},
+ ]},
+ {start_phases,[]}]}.
diff --git a/dudeswave/ebin/dudeswave.appE b/dudeswave/ebin/dudeswave.appE
new file mode 100644
index 0000000..f550b3d
--- /dev/null
+++ b/dudeswave/ebin/dudeswave.appE
@@ -0,0 +1,15 @@
+{application,dudeswave,
+ [{description,"The dudeswave web experience"},
+ {vsn,"1.0.0"},
+ {modules,[dudeswave,dudeswave_app,bigweb_handler,bigweb_supervisor]},
+ {registered,[]},
+ {applications,[kernel,stdlib,erts,cowboy,ranch]},
+ {mod,{bigweb_app,[]}},
+ {env, [
+ {proxy_host,"gemini.abiscuola.com"},
+ {ip,"127.0.0.1"},
+ {port,10443},
+ {certfile, "/var/_erlang/cert/www.abiscuola.com.crt"},
+ {keyfile, "/var/_erlang/private/www.abiscuola.com.key"}
+ ]},
+ {start_phases,[]}]}.
diff --git a/dudeswave/src/Makefile b/dudeswave/src/Makefile
new file mode 100644
index 0000000..7c52e6a
--- /dev/null
+++ b/dudeswave/src/Makefile
@@ -0,0 +1,16 @@
+.PHONY: all clean
+.SUFFIXES: .erl .beam
+
+ERLC?= erlc -server
+
+OBJS= dudeswave.beam dudeswave_app.beam
+OBJS+= dudeswave_supervisor.beam dudeswave_handler.beam
+
+all: ${OBJS}
+
+.erl.beam:
+ ${ERLC} ${ERLOPTS} ${ERLFLAGS} $<
+
+clean:
+ rm -f *.beam
+
diff --git a/dudeswave/src/dudeswave.erl b/dudeswave/src/dudeswave.erl
new file mode 100644
index 0000000..9893223
--- /dev/null
+++ b/dudeswave/src/dudeswave.erl
@@ -0,0 +1,46 @@
+%
+% Copyright (c) 2024 Andrea Biscuola
+%
+% Permission to use, copy, modify, and distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+%
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%
+-module(dudeswave).
+
+-behaviour(gen_server).
+
+% Server start function
+-export([start_link/0]).
+
+% Module callbacks
+-export([init/1, handle_call/3, handle_cast/2, terminate/2]).
+
+%
+% Startup functions
+%
+
+start_link() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+init([]) ->
+ process_flag(trap_exit, true),
+
+ {ok, 0}.
+
+%
+% Callbacks
+%
+
+handle_call(_Msg, _From, State) -> {noreply, State}.
+
+handle_cast(_Msg, State) -> {noreply, State}.
+
+terminate(_Reason, _N) -> ok.
diff --git a/dudeswave/src/dudeswave_app.erl b/dudeswave/src/dudeswave_app.erl
new file mode 100644
index 0000000..5d09989
--- /dev/null
+++ b/dudeswave/src/dudeswave_app.erl
@@ -0,0 +1,44 @@
+%
+% Copyright (c) 2024 Andrea Biscuola
+%
+% Permission to use, copy, modify, and distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+%
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%
+-module(dudeswave_app).
+-behaviour(application).
+
+-export([start/2, stop/1]).
+
+start(_Type, StartArgs) ->
+ {ok, Addr} = case application:get_env(ip) of
+ {ok, AddrConf} -> inet:parse_address(AddrConf);
+ undefined -> undefined
+ end,
+
+ {ok, Port} = application:get_env(port),
+ {ok, Bucket} = application:get_env(bucket),
+
+ Dispatch = cowboy_router:compile([
+ {'_', [{"/", dudeswave_handler, #{bucket => Bucket}}]}
+ ]),
+
+ {ok, ListenerPid} = cowboy:start_tls(dudeswave_listener, [
+ {port, Port},
+ {ip, Addr}
+ ], #{env => #{dispatch => Dispatch}}),
+
+ link(ListenerPid),
+
+ dudeswave_supervisor:start_link(StartArgs).
+
+stop(_State) ->
+ cowboy:stop_listener(dudeswave_listener).
\ No newline at end of file
diff --git a/dudeswave/src/dudeswave_handler.erl b/dudeswave/src/dudeswave_handler.erl
new file mode 100644
index 0000000..7d45eaf
--- /dev/null
+++ b/dudeswave/src/dudeswave_handler.erl
@@ -0,0 +1,89 @@
+%
+% Copyright (c) 2024 Andrea Biscuola
+%
+% Permission to use, copy, modify, and distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+%
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%
+-module(dudeswave_handler).
+-moduledoc """
+The dudeswave GET handler.
+
+This module handles only fetching and returning resources. Anything
+else is considered a bad request, apart from the obvious case of 500
+(internal server error) and 404 (not found).
+""".
+
+-behaviour(cowboy_handler).
+
+-export([init/2, terminate/3]).
+
+-include_lib("storage/include/storage.hrl").
+
+%
+% Protocol functions
+%
+
+init(Req, State) ->
+ {ok, Bucket} = maps:find(bucket, State),
+ Path = cowboy_req:path(Req),
+
+ case cowboy_req:method(Req) of
+ <<"GET">> ->
+ %
+ % Fetch the page from storage
+ %
+ case storage:read(Bucket, Path) of
+ {ok, []} ->
+ Resp = cowboy_req:reply(404,
+ #{<<"content-type">> => <<"text/plain">>}, <<"Not found">>, Req),
+ {ok, Resp, State};
+ %
+ % If we get a resource, send it back to the client with
+ % it's proper mime type.
+ %
+ {ok, [R]} ->
+ {Mime, Bin} = get_final_object(R),
+ Resp = cowboy_req:reply(200, #{<<"content-type">> => Mime},
+ Bin, Req),
+ {ok, Resp, State};
+ {error, _} ->
+ Resp = cowboy_req:reply(500, #{<<"content-type">> => <<"text/plain">>},
+ <<"Internal server error">>, Req),
+ {ok, Resp, State}
+ end;
+ _ ->
+ Resp = cowboy_req:reply(400, #{<<"content-type">> => <<"text/plain">>},
+ <<"Bad request">>, Req),
+ {ok, Resp, State}
+ end.
+
+terminate(_Reason, _Req, _State) -> ok.
+
+-spec get_final_object(Record) -> {Mime, Bin} when
+ Record :: storage:object(),
+ Mime :: binary(),
+ Bin :: binary().
+
+get_final_object(Record) ->
+ case lists:keyfind(mime, 1, Record#object.metadata) of
+ %
+ % We do not know the object mime type. This should never
+ % happen.
+ %
+ false ->
+ {<<"application/octect-stream">>, Record#object.value};
+ %
+ % Everything else, just return it as is.
+ %
+ {mime, Mime} ->
+ {list_to_binary(Mime), Record#object.value}
+ end.
\ No newline at end of file
diff --git a/dudeswave/src/dudeswave_supervisor.erl b/dudeswave/src/dudeswave_supervisor.erl
new file mode 100644
index 0000000..dad62ce
--- /dev/null
+++ b/dudeswave/src/dudeswave_supervisor.erl
@@ -0,0 +1,31 @@
+%
+% Copyright (c) 2024 Andrea Biscuola
+%
+% Permission to use, copy, modify, and distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+%
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%
+-module(dudeswave_supervisor).
+-behaviour(supervisor).
+
+-export([start/0,
+ start_link/1,
+ init/1]).
+
+start() ->
+ spawn(fun() -> supervisor:start_link({local, ?MODULE}, ?MODULE, _Arg = []) end).
+
+start_link(Args) ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, Args).
+
+init([]) ->
+ {ok, {{one_for_one, 3, 10}, [{tag1, {dudeswave, start_link, []}, permanent,
+ 10000, worker, [dudeswave]}]}}.
\ No newline at end of file