Compare commits
16 Commits
25260a5eb8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c779e8fe2 | ||
|
|
bdacd9905e | ||
|
|
85bf788307 | ||
|
|
6944ff05e0 | ||
|
|
b0643a760e | ||
|
|
44fa3976ad | ||
|
|
83e18d7da4 | ||
|
|
904cb233f3 | ||
|
|
66cb85ec1d | ||
|
|
4958944cb8 | ||
|
|
dd60e5459b | ||
|
|
25c260c4a5 | ||
|
|
23d3a97643 | ||
|
|
5e65fad33d | ||
|
|
0172c68e09 | ||
|
|
c3c80b716c |
4
.envrc
4
.envrc
@@ -1 +1,5 @@
|
||||
use flake
|
||||
|
||||
export RELEASE_NODE=frajtano-test@$(hostname -s)
|
||||
export FRAJTANO_DIR=$PWD/.frajtano_state
|
||||
export FRAJTANO_CONFIG=$PWD/config/config.exs
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -29,3 +29,5 @@ frajtano-*.tar
|
||||
/.nix-hex
|
||||
/.nix-mix
|
||||
/result
|
||||
/.frajtano_state
|
||||
/todo
|
||||
|
||||
4
config/config.exs
Normal file
4
config/config.exs
Normal file
@@ -0,0 +1,4 @@
|
||||
import Config
|
||||
|
||||
config :frajtano,
|
||||
initial_backing_agents: []
|
||||
@@ -1,2 +1,5 @@
|
||||
import Config
|
||||
config :frajtano, listen_path: Path.expand(System.get_env("FRAJTANO_DIR")) |> Path.join("agent.sock")
|
||||
|
||||
config :frajtano,
|
||||
listen_path: Path.expand(System.get_env("FRAJTANO_DIR")) |> Path.join("agent.sock")
|
||||
|
||||
|
||||
46
default.nix
46
default.nix
@@ -3,9 +3,10 @@
|
||||
pkgs,
|
||||
mixRelease,
|
||||
elixir,
|
||||
fetchMixDeps,
|
||||
}: let
|
||||
pname = "frajtano";
|
||||
pkg = mixRelease {
|
||||
pkg = mixRelease rec {
|
||||
inherit pname;
|
||||
version = "0.0.1";
|
||||
|
||||
@@ -16,25 +17,60 @@
|
||||
filter = path: _type: baseNameOf path != "flake.nix" && baseNameOf path != "flake.lock";
|
||||
};
|
||||
|
||||
# Adapted from https://blog.eigenvalue.net/nix-rerunning-fixed-output-derivations/
|
||||
# deps hash should change any time ./mix.lock changes, and not otherwise
|
||||
mixFodDeps = let
|
||||
deps = fetchMixDeps {
|
||||
pname = "mix-deps-${pname}";
|
||||
inherit version src;
|
||||
sha256 = {
|
||||
"x86_64-linux" = "sha256-/9Ushiv0z1wJL9FwebUF4gem8IOwlUPnQuDBiXV0Ib8=";
|
||||
"aarch64-darwin" = "sha256-8gFgTV1qYrC3iw5/AyeEOMAYtcEK7wTXie+Z9RDrqUI=";
|
||||
}.${pkgs.system};
|
||||
};
|
||||
hash = builtins.substring 11 32 "${./mix.lock}";
|
||||
in
|
||||
deps.overrideAttrs (attrs: {name = "${deps.name}-${hash}";});
|
||||
|
||||
ELIXIR_MAKE_CACHE_DIR = "/tmp/.elixir-make-cache";
|
||||
meta.mainProgram = pname;
|
||||
};
|
||||
script = pkgs.writeShellScriptBin pname ''
|
||||
set -eu
|
||||
set -e
|
||||
${pkgs.coreutils}/bin/mkdir -p $FRAJTANO_DIR
|
||||
file="$FRAJTANO_DIR/cookie"
|
||||
(umask 077; [ -f "$file" ] || ${pkgs.coreutils}/bin/head -c 128 /dev/urandom | ${pkgs.coreutils}/bin/base64 -w0 > "$file")
|
||||
export RELEASE_COOKIE=$(${pkgs.coreutils}/bin/cat "$file")
|
||||
[ -z $RELEASE_NODE ] && export RELEASE_NODE="frajtano-$(${pkgs.coreutils}/bin/whoami)@$(${pkgs.toybox}/bin/hostname -s)"
|
||||
run() {
|
||||
exec ${lib.getExe pkg} "$@"
|
||||
}
|
||||
|
||||
case $1 in
|
||||
list() {
|
||||
for i in "$@"; do
|
||||
echo "\"$i\", "
|
||||
done
|
||||
}
|
||||
|
||||
case "''${1:-}" in
|
||||
assimilate)
|
||||
run rpc ":ok = \"$(echo -n "$2" | ${pkgs.coreutils}/bin/base64)\" |> Base.decode64!() |> Frajtano.Agent.add_peer()"
|
||||
run rpc ":ok = ~S{$2} |> Frajtano.Agent.assimilate()"
|
||||
;;
|
||||
spawn)
|
||||
shift
|
||||
run rpc ":ok = Frajtano.Agent.spawn_backing_agent({\"/usr/bin/env\", [$(list "$@")]})"
|
||||
;;
|
||||
socket)
|
||||
echo $FRAJTANO_DIR/agent.sock
|
||||
;;
|
||||
*)
|
||||
run "$@"
|
||||
;;
|
||||
esac
|
||||
'';
|
||||
in pkgs.symlinkJoin { name = pname; paths = [ script pkg ]; meta.mainProgram = pname; }
|
||||
in
|
||||
pkgs.symlinkJoin {
|
||||
name = pname;
|
||||
paths = [script pkg];
|
||||
meta.mainProgram = pname;
|
||||
}
|
||||
|
||||
17
flake.lock
generated
17
flake.lock
generated
@@ -2,15 +2,18 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1721924956,
|
||||
"narHash": "sha256-Sb1jlyRO+N8jBXEX9Pg9Z1Qb8Bw9QyOgLDNMEpmjZ2M=",
|
||||
"path": "/nix/store/xblysc3prg1zqsi59fr36nj6wdiqryzp-source",
|
||||
"rev": "5ad6a14c6bf098e98800b091668718c336effc95",
|
||||
"type": "path"
|
||||
"lastModified": 1730272153,
|
||||
"narHash": "sha256-B5WRZYsRlJgwVHIV6DvidFN7VX7Fg9uuwkRW9Ha8z+w=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2d2a9ddbe3f2c00747398f3dc9b05f7f2ebb0f53",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
|
||||
63
flake.nix
63
flake.nix
@@ -1,5 +1,6 @@
|
||||
{
|
||||
description = "frajtano: an ssh agent multiplexer";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
@@ -15,7 +16,11 @@
|
||||
ownPkgs = self.packages.${system};
|
||||
});
|
||||
in {
|
||||
devShells = eachSystem ({pkgs, ownPkgs, ...}: {
|
||||
devShells = eachSystem ({
|
||||
pkgs,
|
||||
ownPkgs,
|
||||
...
|
||||
}: {
|
||||
default = pkgs.beam.packages.erlang_26.callPackage ./shell.nix {
|
||||
inherit ownPkgs;
|
||||
};
|
||||
@@ -23,5 +28,61 @@
|
||||
packages = eachSystem ({pkgs, ...}: {
|
||||
default = pkgs.beam.packages.erlang_26.callPackage ./default.nix {};
|
||||
});
|
||||
homeModules.default = {
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
cfg = config.bluepython508.frajtano;
|
||||
in {
|
||||
options.bluepython508.frajtano = {
|
||||
enable = lib.mkEnableOption "frajtano";
|
||||
dir = lib.mkOption {
|
||||
description = "directory in which to place the listening socket";
|
||||
default = "${config.home.homeDirectory}/.ssh/frajtano";
|
||||
type = lib.types.path;
|
||||
};
|
||||
initialBackingAgents = lib.mkOption {
|
||||
description = "initially spawned backing_agents - will be passed to /usr/bin/env";
|
||||
type = with lib.types; listOf (listOf str);
|
||||
default = [];
|
||||
};
|
||||
};
|
||||
|
||||
config = let
|
||||
backing_agents = lib.strings.concatMapStringsSep ", " (args: ''{:spawn, {"${pkgs.coreutils}/bin/env", [${lib.concatMapStringsSep ", " (s: ''~S{${s}}'') args}]}}'') cfg.initialBackingAgents;
|
||||
configFile = pkgs.writeText "config.exs" ''
|
||||
import Config
|
||||
config :frajtano, initial_backing_agents: [${backing_agents}]
|
||||
'';
|
||||
in lib.mkIf cfg.enable {
|
||||
home.sessionVariables.FRAJTANO_DIR = cfg.dir;
|
||||
home.packages = [self.packages.${pkgs.system}.default];
|
||||
|
||||
systemd.user.services.frajtano = {
|
||||
Unit.Description = "frajtano";
|
||||
Unit.After = ["default.target"];
|
||||
Service.Type = "notify";
|
||||
Service.Environment = ["'FRAJTANO_DIR=${cfg.dir}'" "'FRAJTANO_CONFIG=${configFile}'"];
|
||||
Service.ExecSearchPath = ["${self.packages.${pkgs.system}.default}/bin"];
|
||||
Service.ExecStart = "frajtano start";
|
||||
Install.WantedBy = ["default.target"];
|
||||
};
|
||||
|
||||
launchd.agents.frajtano = {
|
||||
enable = true;
|
||||
config = {
|
||||
EnvironmentVariables = {
|
||||
FRAJTANO_DIR = "${cfg.dir}";
|
||||
FRAJTANO_CONFIG = "${configFile}";
|
||||
};
|
||||
ProgramArguments = ["${self.packages.${pkgs.system}.default}/bin/frajtano" "start"];
|
||||
RunAtLoad = true;
|
||||
KeepAlive = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
78
lib/agent.ex
78
lib/agent.ex
@@ -1,6 +1,7 @@
|
||||
defmodule Frajtano.Agent do
|
||||
alias Frajtano.Peer
|
||||
alias Frajtano.BackingAgent
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
def start_link(args) do
|
||||
GenServer.start_link(__MODULE__, nil, [{:name, __MODULE__} | args])
|
||||
@@ -8,63 +9,74 @@ defmodule Frajtano.Agent do
|
||||
|
||||
@impl true
|
||||
def init(_) do
|
||||
{:ok,
|
||||
%{
|
||||
keys: %{}
|
||||
}}
|
||||
{
|
||||
:ok,
|
||||
%{},
|
||||
}
|
||||
end
|
||||
|
||||
def initial_backing_agents() do
|
||||
initial_backing_agents = Application.fetch_env!(:frajtano, :initial_backing_agents)
|
||||
|> Enum.map(&GenServer.call(__MODULE__, {:add_backing_agent, &1}))
|
||||
Logger.info("Started initial backing_agents: #{inspect initial_backing_agents}")
|
||||
end
|
||||
|
||||
# select: list of specs, where specs are a tuple of match, guards, and outputs
|
||||
# match is {key, pid, value}, :"$1" is a match variable
|
||||
def backing_agent_paths() do
|
||||
Registry.select(Frajtano.BackingAgents, [{{:"$1", :_, :_}, [], [:"$1"]}])
|
||||
end
|
||||
|
||||
def backing_agent_pids() do
|
||||
Registry.select(Frajtano.BackingAgents, [{{:_, :"$1", :_}, [], [:"$1"]}])
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:identities}, _from, state) do
|
||||
def handle_call({:identities}, _from, _state) do
|
||||
idents =
|
||||
for(
|
||||
{_, peer, :worker, _} <- DynamicSupervisor.which_children(Frajtano.Peer),
|
||||
is_pid(peer),
|
||||
do: peer
|
||||
Task.async_stream(
|
||||
backing_agent_pids(),
|
||||
&{&1, BackingAgent.identities(&1)},
|
||||
ordered: false,
|
||||
on_timeout: :kill_task
|
||||
)
|
||||
|> Task.async_stream(fn peer ->
|
||||
with {:ok, idents} <- Peer.identities(peer),
|
||||
do: {:ok, {idents, peer}}
|
||||
end)
|
||||
|
||||
|
||||
# Double :ok-wrapping because of Task.async_stream
|
||||
idents = (for {:ok, {:ok, {idents, peer}}} <- idents, do: {idents, peer})
|
||||
idents = for {:ok, {backing_agent, {:ok, idents}}} <- idents, do: {idents, backing_agent}
|
||||
|
||||
{
|
||||
:reply,
|
||||
{:ok, for({idents, _} <- idents, ident <- idents, do: ident)},
|
||||
%{
|
||||
state
|
||||
| keys:
|
||||
for({idents, peer} <- idents, {key, _comment} <- idents, into: %{}, do: {key, peer})
|
||||
}
|
||||
{:ok, idents |> Enum.flat_map(&elem(&1, 0)) |> Enum.uniq},
|
||||
for({idents, backing_agent} <- idents, {key, _comment} <- idents, into: %{}, do: {key, backing_agent})
|
||||
}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:sign, {key, _, _} = req}, _from, state) do
|
||||
{:reply, Peer.sign(state.keys[key], req), state}
|
||||
{:reply, BackingAgent.sign(state[key], req), state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:add_peer, path}, _from, state) do
|
||||
# TODO: deduplicate peers by socket path
|
||||
case Peer.start(path) do
|
||||
def handle_call({:add_backing_agent, spec}, _from, state) do
|
||||
case BackingAgent.start(spec) do
|
||||
{:ok, _} -> {:reply, :ok, state}
|
||||
{:error, error} -> {:reply, {:error, error}, state}
|
||||
end
|
||||
end
|
||||
|
||||
def identities(agent \\ __MODULE__) do
|
||||
GenServer.call(agent, {:identities})
|
||||
def identities() do
|
||||
GenServer.call(__MODULE__, {:identities})
|
||||
end
|
||||
|
||||
def sign(agent \\ __MODULE__, request) do
|
||||
GenServer.call(agent, {:sign, request})
|
||||
def sign(request) do
|
||||
# Signing can take some time, as a password may need to be entered or similar
|
||||
GenServer.call(__MODULE__, {:sign, request}, :infinity)
|
||||
end
|
||||
|
||||
def add_peer(agent \\ __MODULE__, path) do
|
||||
GenServer.call(agent, {:add_peer, path})
|
||||
def assimilate(path) do
|
||||
GenServer.call(__MODULE__, {:add_backing_agent, {:socket, path}})
|
||||
end
|
||||
|
||||
def spawn_backing_agent(spec) do
|
||||
GenServer.call(__MODULE__, {:add_backing_agent, {:spawn, spec}})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,10 +17,13 @@ defmodule Frajtano.Supervisor do
|
||||
@impl true
|
||||
def init(:ok) do
|
||||
children = [
|
||||
{DynamicSupervisor, name: Frajtano.BackingAgentSupervisor},
|
||||
{Registry, keys: :unique, name: Frajtano.BackingAgents},
|
||||
Frajtano.Agent,
|
||||
{Frajtano.Listener, [Application.fetch_env!(:frajtano, :listen_path)]},
|
||||
{Task.Supervisor, name: Frajtano.ClientSupervisor},
|
||||
{DynamicSupervisor, name: Frajtano.Peer}
|
||||
{Frajtano.Listener, [Application.fetch_env!(:frajtano, :listen_path)]},
|
||||
{Task, &Frajtano.Agent.initial_backing_agents/0},
|
||||
:systemd.ready(),
|
||||
]
|
||||
|
||||
Supervisor.init(children, strategy: :one_for_one)
|
||||
|
||||
136
lib/peer.ex
136
lib/peer.ex
@@ -1,36 +1,67 @@
|
||||
defmodule Frajtano.Peer do
|
||||
defmodule Frajtano.BackingAgent do
|
||||
alias Frajtano.Proto
|
||||
require Logger
|
||||
use GenServer, restart: :temporary
|
||||
|
||||
def start(path) do
|
||||
DynamicSupervisor.start_child(Frajtano.Peer, {__MODULE__, path})
|
||||
end
|
||||
defmodule Spawner do
|
||||
use Supervisor, restart: :permanent
|
||||
|
||||
def start_link(path) do
|
||||
GenServer.start_link(__MODULE__, path)
|
||||
def start_link(spec) do
|
||||
Supervisor.start_link(__MODULE__, spec, [])
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(path) do
|
||||
def init({executable, args}) do
|
||||
Temp.track!()
|
||||
path = Path.join(Temp.mkdir!(), "agent.sock")
|
||||
|
||||
children = [
|
||||
Supervisor.child_spec({MuonTrap.Daemon, [executable, args ++ [path]]}, restart: :temporary, significant: true),
|
||||
Supervisor.child_spec({Frajtano.BackingAgent, {path, :spawned, {executable, args}}}, restart: :permanent)
|
||||
]
|
||||
|
||||
Supervisor.init(children, strategy: :one_for_all, auto_shutdown: :any_significant)
|
||||
end
|
||||
end
|
||||
|
||||
def start({:socket, path}) do
|
||||
DynamicSupervisor.start_child(Frajtano.BackingAgentSupervisor, {__MODULE__, {path}})
|
||||
end
|
||||
|
||||
def start({:spawn, spec}) do
|
||||
Logger.info("Spawning #{inspect spec}")
|
||||
DynamicSupervisor.start_child(Frajtano.BackingAgentSupervisor, {Spawner, spec})
|
||||
end
|
||||
|
||||
def start_link({path}) do
|
||||
GenServer.start_link(__MODULE__, {path}, name: {:via, Registry, {Frajtano.BackingAgents, path}})
|
||||
end
|
||||
def start_link({_, _, _} = spec) do
|
||||
GenServer.start_link(__MODULE__, spec, name: {:via, Registry, {Frajtano.BackingAgents, spec}})
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init({path}) do
|
||||
{:ok, conn} = :gen_tcp.connect({:local, path}, 0, [:binary, active: :once])
|
||||
{:ok, %{conn: conn, clients: :queue.new(), buffer: <<>>}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(packet, from, %{conn: conn, clients: clients} = state) do
|
||||
case :gen_tcp.send(conn, Proto.encode(packet)) do
|
||||
:ok ->
|
||||
{:noreply, %{state | clients: :queue.in(from, clients)}}
|
||||
|
||||
{:error, :closed} ->
|
||||
{:noreply, state, {:continue, :closed}}
|
||||
|
||||
{:error, e} ->
|
||||
raise(e)
|
||||
def init({path, :spawned, spec}) do
|
||||
if File.exists?(path) do
|
||||
{:ok, conn} = :gen_tcp.connect({:local, path}, 0, [:binary, active: :once])
|
||||
Logger.info("Connected to #{inspect spec}")
|
||||
{:ok, %{conn: conn, clients: :queue.new(), buffer: <<>>}}
|
||||
else
|
||||
Process.sleep(100)
|
||||
init({path, :spawned, spec})
|
||||
end
|
||||
end
|
||||
|
||||
def reply({client, ref}, msg) do
|
||||
send(client, {ref, msg})
|
||||
end
|
||||
|
||||
defp handle_messages(%{clients: clients, buffer: buffer} = state) do
|
||||
case Proto.decode(buffer) do
|
||||
{nil, buffer} ->
|
||||
@@ -38,42 +69,85 @@ defmodule Frajtano.Peer do
|
||||
|
||||
{msg, buffer} ->
|
||||
{{:value, client}, clients} = :queue.out(clients)
|
||||
GenServer.reply(client, {:ok, msg})
|
||||
reply(client, {:ok, msg})
|
||||
handle_messages(%{state | clients: clients, buffer: buffer})
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:send, packet, from}, %{conn: conn, clients: clients} = state) do
|
||||
case :gen_tcp.send(conn, Proto.encode(packet)) do
|
||||
:ok ->
|
||||
{:noreply, %{state | clients: :queue.in(from, clients)}}
|
||||
|
||||
{:error, e} ->
|
||||
{:noreply, state, {:continue, {:error, e}}}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:tcp, conn, msg}, %{conn: conn, buffer: buffer} = state) do
|
||||
:inet.setopts(conn, active: :once)
|
||||
buffer = buffer <> msg
|
||||
|
||||
handle_messages(%{ state | buffer: buffer })
|
||||
handle_messages(%{state | buffer: buffer})
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:tcp_closed, _}, state) do
|
||||
{:noreply, state, {:continue, :closed}}
|
||||
{:noreply, state, {:continue, {:error, :closed}}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_continue(:closed, %{clients: clients}) do
|
||||
def handle_info(:timeout, state) do
|
||||
{:noreply, state, {:continue, {:error, :closed}}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_continue({:error, e}, %{clients: clients}) do
|
||||
clients
|
||||
|> :queue.to_list()
|
||||
|> Enum.each(&GenServer.reply(&1, {:error, :closed}))
|
||||
|> Enum.each(&reply(&1, {:error, e}))
|
||||
|
||||
{:stop, :closed, %{}}
|
||||
{:stop, {:error, e}, %{}}
|
||||
end
|
||||
|
||||
def identities(peer) do
|
||||
with {:ok, {:agent_identities_answer, identities}} <-
|
||||
GenServer.call(peer, {:agentc_request_identities, nil}),
|
||||
do: {:ok, identities}
|
||||
def identities(backing_agent) do
|
||||
ref = make_ref()
|
||||
send(backing_agent, {:send, {:agentc_request_identities, nil}, {self(), ref}})
|
||||
# Needs to be less than the timeout in Frajtano.Agent.identities on the Task.async_stream call
|
||||
# That's 5000 by default
|
||||
timer = Process.send_after(backing_agent, :timeout, 4500)
|
||||
|
||||
receive do
|
||||
{^ref, msg} ->
|
||||
Process.cancel_timer(timer)
|
||||
msg
|
||||
end
|
||||
|> case do
|
||||
{:ok, {:agent_identities_answer, identities}} -> {:ok, identities}
|
||||
{:ok, {:agent_failure, nil}} -> {:error, :agent_failure}
|
||||
{:ok, msg} -> raise("Unexpected message #{inspect(msg)}")
|
||||
{:error, e} -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def sign(peer, request) do
|
||||
with {:ok, {:agent_sign_response, signature}} <-
|
||||
GenServer.call(peer, {:agentc_sign_request, request}),
|
||||
do: {:ok, signature}
|
||||
def sign(backing_agent, request) do
|
||||
# Signing may take some time, as a password may need to be entered or similar
|
||||
# There is therefore no timeout
|
||||
# If something requests identities afterwards, it will timeout, which also kills this signature request
|
||||
# The SSH agent protocol strict ordering leaves fun problems with timeouts, as it turns out
|
||||
ref = make_ref()
|
||||
send(backing_agent, {:send, {:agentc_sign_request, request}, {self(), ref}})
|
||||
|
||||
receive do
|
||||
{^ref, msg} -> msg
|
||||
end
|
||||
|> case do
|
||||
{:ok, {:agent_sign_response, signature}} -> {:ok, signature}
|
||||
{:ok, {:agent_failure, nil}} -> {:error, :agent_failure}
|
||||
{:ok, msg} -> raise("Unexpected message #{inspect(msg)}")
|
||||
{:error, e} -> {:error, e}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
14
mix.exs
14
mix.exs
@@ -7,19 +7,27 @@ defmodule Frajtano.MixProject do
|
||||
version: "0.0.1",
|
||||
elixir: "~> 1.16",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
deps: deps(),
|
||||
releases: [
|
||||
frajtano: [
|
||||
config_providers: [{Config.Reader, {:system, "FRAJTANO_CONFIG", ""}}]
|
||||
],
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger],
|
||||
env: [listen_path: nil],
|
||||
mod: {Frajtano, []}
|
||||
]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[]
|
||||
[
|
||||
{:muontrap, "~> 1.0"},
|
||||
{:temp, "~> 0.4"},
|
||||
{:systemd, "~> 0.6"},
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
8
mix.lock
8
mix.lock
@@ -1 +1,7 @@
|
||||
|
||||
%{
|
||||
"elixir_make": {:hex, :elixir_make, "0.8.4", "4960a03ce79081dee8fe119d80ad372c4e7badb84c493cc75983f9d3bc8bde0f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "6e7f1d619b5f61dfabd0a20aa268e575572b542ac31723293a4c1a567d5ef040"},
|
||||
"enough": {:hex, :enough, "0.1.0", "0254710c52d324e2dadde54cb56fbb80a792c2eb285669b8379efd0752bf89f0", [:rebar3], [], "hexpm", "0460c7abda5f5e0ea592b12bc6976b8a5c4b96e42f332059cd396525374bf9a1"},
|
||||
"muontrap": {:hex, :muontrap, "1.5.0", "bf5c273872379968615a39974458328209ac97fa1f588396192131ff973d1ca2", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "daf605e877f60b5be9215e3420d7971fc468677b29921e40915b15fd928273d4"},
|
||||
"systemd": {:hex, :systemd, "0.6.2", "aaa24f1e3e6cb978c45369768b1abd766a0dbff637ed61254ca64797bcec9963", [:rebar3], [{:enough, "~> 0.1.0", [hex: :enough, repo: "hexpm", optional: false]}], "hexpm", "5062b911800c1ab05157c7bf9a9fbe23dd24c58891c87fd12d2e3ed8fc1708b8"},
|
||||
"temp": {:hex, :temp, "0.4.9", "eb6355bfa7925a568b3d9eb3bb57e89aa6d2b78bfe8dfb6b698e090631b7f41f", [:mix], [], "hexpm", "bc8bf7b27d9105bef933ef4bf4ba37ac6b899dbeba329deaa88c60b62d6b4b6d"},
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
pkgs,
|
||||
ownPkgs,
|
||||
mkShell,
|
||||
elixir,
|
||||
elixir-ls,
|
||||
inotify-tools,
|
||||
}:
|
||||
mkShell {
|
||||
inputsFrom = [ ownPkgs.default ];
|
||||
packages =
|
||||
[elixir-ls]
|
||||
[elixir elixir-ls]
|
||||
++ lib.lists.optional (pkgs.system == "x86_64-linux") inotify-tools
|
||||
++ lib.lists.optionals (pkgs.system == "aarch64-darwin") (with pkgs.darwin.apple_sdk.frameworks; [
|
||||
CoreFoundation
|
||||
|
||||
Reference in New Issue
Block a user