anchor.diff:  
   protocol_error(Reason, State) ->
       {error, Reason, State}.
  +
  +%% Create a frame given the options (fields, effectively) as an alist
  +opts(F, []) ->
  +    F;
  +opts(F, [Opt |Rest]) ->
  +    opts(opt(F, Opt), Rest).
  +
  +opt(C = #connect{}, Opt) ->
  +    connect_opt(C, Opt).
  +
  +-type(connect_option() ::
  +      {client_id, client_id()}
  +    | {username, binary() | iolist()}
  +    | {password, binary() | iolist()}).
  +
  +-spec(connect_opt(#connect{}, connect_option()) -> #connect{}).
  +connect_opt(C = #connect{}, {client_id, Id}) ->
  +    C#connect{ client_id = iolist_to_binary([Id]) };
  +connect_opt(C = #connect{}, {username, User}) ->
  +    C#connect{ username = iolist_to_binary([User])};
  +connect_opt(C = #connect{}, {password, Pass}) ->
  +    C#connect{ password = iolist_to_binary(Pass)}.
  +
  +-type(address() ::
  +        {host(), inet:port_number()}
  +      | host()).
  +
  +-type(host() :: inet:ip_address() | inet:hostname()).
  +
  +-spec(make_address(address()) -> {host(), inet:port_number()}).
  +make_address(Whole = {Host, Port}) ->
  +    Whole;
  +make_address(Host) ->
  +    {Host, 1883}.
  
positive.code:           frame_buf = <<>>
           }).
  
  start_link() ->
      gen_fsm:start_link(?MODULE, [], []).
  
  connect(Conn, Host, Port, ConnectFrame) ->
      {ok, Sock} = gen_tcp:connect(Host, Port,
                                   [{active, false},
                                    binary]),
      open(Conn, Sock, ConnectFrame).
  
  %% NB this assumes that the calling process controls the socket
  open(Conn, Sock, ConnectFrame) ->
      ok = gen_tcp:controlling_process(Sock, Conn),
      gen_fsm:sync_send_event(Conn, {open, Sock, ConnectFrame}).
  
  
  %%% gen_fsm callbacks
  
  
negative.code:     {ok, unopened, #state{}}.
  
  %% states
  
  %% worth putting specs on these?
  
  unopened({open, Socket, Connect}, From,
           S0 = #state{ socket = undefined }) ->
  