anchor.diff:      lager:error("cast ~p", [E]),
       {noreply, State}.
   
  -handle_info({reload, File}, State) ->
  -    ets:delete_all_objects(?MODULE),
  +% monitor password file changes
  +handle_info(monitor, State=#state{filename=File, last_modified=LastMod}) ->
  +    LastMod2 = filelib:last_modified(File),
  +    if LastMod2 =/= LastMod -> reload(File); true -> ok end,
   
  -    % TODO: handle errors
  -    {ok, F} = file:open(File, [read,binary]),
  -    Count = fill_ets(F, file:read_line(F), 0),
  -    file:close(F),
  +    erlang:send_after(?DFT_MONITOR_INTERVAL, self(), monitor),
  +    {noreply, State#state{last_modified=LastMod2}};
   
  -    lager:debug("password file reloaded (~p lines found)", [Count]),
  -    {noreply, State};
  +% force reloading file
  +handle_info(reload, State=#state{filename=File}) ->
  +    reload(File),
  +    {noreply, State#state{last_modified=filelib:last_modified(File)}};
   
   handle_info(E, State) ->
       lager:error("info ~p", [E]),
  
positive.code: %% PRIVATE FUNS
  %%
  
  fill_ets(_, eof, Count) ->
      Count;
  fill_ets(F, {ok, Line}, Count) ->
  
negative.code: % gen_server API
  -export([start_link/1, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
  
  -define(DFT_MONITOR_INTERVAL, 60000). % 1 minute
  
  -record(state, {
      filename,
      last_modified = undefined
  }).
  
  start_link(Args) ->
      gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []).
  
  init(Args) ->
      ets:new(?MODULE, [set, named_table, private]),
      % 1st monitor will trigger file load
      erlang:send_after(50, self(), monitor),
      {ok, #state{filename=proplists:get_value(file, Args)}}.
  
  %%
  %% PUBLIC API
  