Hgadmin is a set of tools intended to simplify
    user and group management, 
    authentication, and
    access control
for shared mercurial repositories.

Hgadmin's intended scenario is:

 - a set of users (optionally organized in groups)
 - a set of repositories (all residing below one directory)
 - a user may be allowed to read or write to some repositories
 - a user may be allowed to create new repositories in some subdirectory
 - read/write access is possible via ssh and https
 - creation is possible only via ssh

Hgadmin for users


   Hgadmin as a tool is mainly intended to ease system administration.

   End users should not notice anything related to hgadmin, except
   hopefully that "everything just works"(tm)


   As end user, you only need a computer with mercurial.

   A repository server managed with hgadmin will typically export
   several repositories. These repositories can be accessed via ssh or
   http(s); hgadmin will manage access control and authentication for
   both access methods at the same time.

   For http(s) access, typically a password will be needed.
   Currently, hgadmin supports htpasswd - files as used by apache and
   several other web servers. An end user will therefore have to set a
   password. Setting this password is out of scope of hgadmin; contact
   your system administrators for related information.

   For ssh access, a ssh key is needed. An end user will have to
   create a ssh key and submit its public part to the administrators.

   Additionally, the administrators will have to configure which
   repositories the user has read- and commit-access.


   After setting a password and submitting a ssh key, an end user can
   use the repositories managed by hgadmin exactly like any other
   remote mercurial repository.

   Creating new repositories on the server managed by hgadmin also
   works exactly as expected; even if this functionality is supported
   only via ssh.

Hgadmin for administrators

Hgadmin requires a "Configuration Directory" to keep a user/group
database, access configuration, password-hashfile (htpasswd-format) and ssh-keys.

It uses this information to do the following things:

  - replace the htpasswd file used by the webserver, if password
    information changes
  - generate access configuration for each managed repository by
    changing the settings in the "[web]" section of the repository
    configuration; namely, the values of the "deny_read",
    "allow_read", "deny_push" and "allow_push" options.
  - generate a authorized_keys file for ssh, that allowes the known
    keys to call a special wrapper script
  - said wrapper script uses the same contents of the "[web]" section
    of the repository configuration to determine whether a user can
    access the repository.

Comparison with other tools
- mercurial-server
  hgadmin supports groups (which mercurial-server does not)
  hgadmin supports users, whereas mercurial-server just does access control for keys
  hgadmin supports access via https 
  hgadmin is about as complicated to set up as mercurial-server

- rhodecode
  hgadmin supports access via ssh (which rhodecode does not)
  hgadmin does not require a web server or some complicated setup


After installation, the hgadmin scripts are installed on your
system, but not yet configured.

Basic installation consists of these steps:

  - Clone the hgadmin repository
	Example: hg clone https://www.hawo-net.de/~jakob/repos/hgadmin /opt/hgadmin

  - Put the hgadmin executables somewhere in the PATH
	Example: ln -s /opt/hgadmin/{hgadmin,mkconfrepo,hg-ssh} /usr/local/bin

Additional preparation:

hgadmin is supposed to run under a dedicated user account.

Usually, one would use "hg" as username. 

For the sake of the example, let us assume that "hg" has his home
directory under "/home/hg"; the repositories to serve are under "/var/repos".

If the user does not yet exist, create it:
   useradd -m -d /home/hg hg
   su - hg mkdir -p .ssh
   su - hg chmod go-rwx .ssh

Create the repository directory:

   mkdir -p /var/repos
   chown hg:hg /var/repos

hgadmin will automatically overwrite the .ssh/authorized_keys file.

If you want static content in that file, use ".ssh/authorized_keys_const".

If the user already exists and has important entries in the
authorized_keys file: 

   su - hg cp .ssh/authorized_keys  .ssh/authorized_keys_const

If you want to be able to login with a ssh key (located at "/tmp/admin_ssh_key"):

   su - hg cat /tmp/admin_ssh_key >> .ssh/authorized_keys_const
   su - hg cat /tmp/admin_ssh_key >> .ssh/authorized_keys

Usually, you want the hgadmin configuration directory managed by hg. 
There is a nice helper script for that:
  su - hg mkconfrepo --addserverhooks hgadmin-config


All the configuration of hgadmin resides in one directory.


For this example, the managed repositories reside under "/var/repos".

The hgadmin configuration directory is at "/home/hg/hgadmin-config";
the hgadmin scripts are at "/opt/hgadmin"

The ssh authorized_keys file is at "/home/hg/.ssh/authorized_keys";
the htpasswd file is at "/var/www/repos/htpasswd".

The managed repositories belong to the "hg" user, which also has read
access to the configuration directory (and all its files) and is able
to change the htpasswd file.

'config' file

The main configuration file is "/home/hg/hgadmin-config/config":
>   [paths]
>   repopath = /var/repos
>   htpasswdpath = /var/www/repos/htpasswd
>   hg-ssh = /opt/hgadmin/hg-ssh
>   sshauthkeyspath = /home/hg/.ssh/authorized_keys
>   [groups]
>   group1 = user4, user5
>   group2 = user2, user3, user4
>   [users]
>   users = user1, user2,
>    # strange user with own description:
>      user3,
>    # another user description
>      user4
>    # and another user description
>      user5

It contains the relevant paths, a list of users and a list of groups,
together with the users they contain.

'access' file

The access configuration is at "/home/hg/hgadmin-config/access":
>   [/**]
>   user1 = rw
>   user2 = r
>   [/sandbox/*]
>   user2 = rw
>   user3 = rw
>   user4 = r
>   [/sandbox/foo]
>   user1 = r
>   user4 = rw 
>   user3 = 
>   @group1 = rw
>   user5 = r

In this configuration, presume there are three repositories:
  (a) "/var/repos/test"
  (b) "/var/repos/sandbox/bar"
  (c) "/var/repos/sandbox/foo" 

Access rights for a particular user to some repository are taken from
the pattern that most closestly matches the repository path and
mentions the user (or a group he is in). If a user and a group he is
in are mentioned, the user will get all rights specified.


(a) can be read by user1 and user2, and written by user1.  

(b) can be read by user1, user2, user3 and user4. it can be written by
user1, user2 and user3.

(c) can be read by user1, user2, user4 and user5. It can be written by
user2, user4 and user5, but not by user1 or user3.

'hgrc' file:

The 'hgrc' file is at "/home/hg/hgadmin-config/hgrc":
>   [hooks]
>   incoming.email = /my/email/hook
>   [web]
>   allow_archive = gz, zip
>   contact = "admin" <admin@somedomain>

Its contents get inserted into the repository local mercurial
configuration for every managed repository.

In this case, it means that the (email sending) hook is executed every
time someone pushes changes to a repository, and that the hgweb cgi
will allow downloading of the specified archives and will show the
configured contact.

'htpasswd' file:

The 'htpasswd' file is at "/home/hg/hgadmin-config/htpasswd".

The contents of the 'htpasswd' file are irrelevant to hgadmin. It is
simply copied to the location specified in the 'config' file (Section
"paths", configuration setting "htpasswdpath").

Description of the Configuration Directory

There are four configuration files and one directory for the users'
ssh keys.

'config' file

The first configuration file is named 'config'. Its format is standard
ini-format (as understood by python's ConfigParser).

There are three sections in the 'config'-file, named 'paths', 'groups' and 'users'.

The section 'paths' has six possible settings:

  - 'repopath' -- directory path that contains the managed repositories
  - 'htpasswdpath' -- path to the htpasswdfile, if present
  - 'sshauthkeyspath' -- path to the authorized_keys file
  - 'hg-ssh' -- path to the hg-ssh script
  - 'additionalusersfile' -- file containing a comma-separated list of additional users
  - 'additionalgroupsfile' -- file containing additional groups

The section 'users' has exactly one setting, named 'users'.  Its value
is a comma separated list of the users. The users specified in
'additionalusersfile' are added to the list of users.

The section 'groups' contains the groups. A groupname is encoded in
the name of a setting; the users belonging to the group end up in the
value of the setting. The groups specified in 'additionalgroupsfile'
are added to these groups. 'additionalgroupsfile' contains one or more
lines consisting of:
where GROUPNAME is the name of the group and USERLIST is a
comma-separated list of members of the group.

If the "htpasswdpath" option is not specified, hgadmin will not update
it. Likewise, if either the "hg-ssh" option or the "sshauthkeyspath"
option are not specified, hgadmin will not update the authorized_keys
file.  If the "repopath" option is missing, hgadmin will simply
complain and do nothing.

If user or group names contain invalid characters, or if groups
reference unknown users, hgadmin will display a warning and ignore
these users and/or groups.

'access' file:

The names of the configuration sections refer to repositories.

A configuration section must start with '/'. It is interpreted
relative to the path configured in the 'repopath' setting in the

If a name ends with '/**', it is a wildcard referring to this
directory and every directory below it down to arbitrary depth.

If a name ends with a '/*', it is a wildcard referring to
this directory and everything immediately inside it.

If a name does not end with a '*' character, it directly and
immediately refers to a repository.

The values of one configuration section are the access rights granted
to users.

The syntax (as seen above) is:

where USER is a (valid) user name, GROUP is a (valid) group name, and
ACCESSRIGHTS is either empty or one of the following strings: 'deny',
'r', 'rw' or 'rwC'.
  - 'deny' and the empty string both mean that the user or group has
    no rights.
  - 'r' means read access (i.e. the user or group is allowed to pull
    from the repository)
  - 'rw' means read and write access, i.e. the user or group is
    allowed to pull from and to push to the repository
  - 'rwC' means read/write/create access, i.e. the user or group is
    allowed to pull from and push to the repository, and allowed to
    create new repositories).

This means that 'rw' implies 'r', and 'rwC' implies 'rw'. 

Leading or trailing whitespace is ignored when interpreting

When determining access rights to a repository, the sections are
examined in order from best match to worst match.

The first section that mentions either the user or a group that
contains the user is used to determine access rights; no further
sections are examined.

When this section mentions a user and one or more groups that contain
that user, the user has all the access rights specified for himself
and the groups that contain him.

'hgrc' file:

If present, the settings in this file get included into the settings
in every managed mercurial repository. Refer to the mercurial
documentation for its possible contents. 

Note that the settings "web.allow_read", "web.allow_push",
"web.deny_read" and "web.deny_push" will be overridden by hgadmin; you
may specify them but they will not have any effect.

'htpasswd' file:

If present, and if the configuration setting "htpasswdpath" is
present, this file will be copied to the location specified in this
configuration setting. Other than that, the contents of this file will
be ignored.

Other relevant files:


The hgadmin script automatically overwrites the contents of
"~/.ssh/authorized_keys" with the keys in the "keys" subdirectory.

If you want static content in that file, put it in
'~/.ssh/authorized_keys_const'.  It will be inserted at the beginning
of "~/.ssh/authorized_keys" every time that file is regenerated.


The ".hg/hgrc" file in every managed repository is automatically
overwritten. If you want static repository-local configuration, put it
in ".hg/hgrc_prefix". If present, the settings in that file will get
included into the settings of the managed repository.

The settings will be included after the settings of the "hgrc" file in
the hgadmin configuration directory, so they can override global

Refer to the mercurial documentation for its possible contents.

Note that the settings "web.allow_read", "web.allow_push",
"web.deny_read" and "web.deny_push" will be overridden by hgadmin; you
may specify them but they will not have any effect.

The scripts

This package consists of three scripts, hg-ssh, hgadmin and mkconfrepo.


hg-ssh is supposed to be invoked by the ssh-server as a wrapper around
the "ordinary" hg, via the "command=..." option in the authorized_keys

Note that hg-ssh is only used internally by hgadmin. Ordinarily, a
system administrator need not care about it.

Given that hg-ssh is the only part that faces direct communication
with external users, here a description of its actions:

hg-ssh requires three arguments on execution. Its first argument is
the directory where the repositories are located. Its second argument
ist the user associated with the ssh-key. Its third argument is the
directory where the configuration data for hgadmin resides.

hg-ssh will parse the environment variable SSH_ORIGINAL_COMMAND (as
set by ssh) for an attempt at repository creation or access. If
neither matches, hg-ssh will exit. 

If the value of SSH_ORIGINAL_COMMAND looks like an attempt at
repository creation (i.e. it matches the regex '^hg init .*$'), hg-ssh
will check whether repository creation is enabled. If so, it will pass
the name of the repository to be created, the creating user and the
configuration directory to the hgadmin script.

If the value of SSH_ORIGINAL_COMMAND looks like an attempt at
repository access (i.e. it matches the regex '^hg -R .* serve --stdio$'), 
hg-ssh will check whether a directory with this name exists and looks
like it is managed by hgadmin (by checking the presence of the file
'.hg/ADMINISTRATED_BY_HGADMIN'). If said file is not present, hg-ssh
will abort.  Next, hg-ssh will attempt to parse the repository-local
configuration and check whether its username is present in the list of
users allowed to read from or write to the repository. If the user is
not allowed to read, hg-ssh will abort.  If the user is not allowed to
write to the repository, hg-ssh will execute:

'hg serve --stdio -R REPO --config hooks.prechangegroup=false --config hooks.pretxnchangegroup=false'

Otherwise (i.e. if the user is allowed to read and write) hg-ssh will

'hg serve --stdio -R REPO'.

This hg-ssh script differs from the hg-ssh script found in the
mercurial distribution, in that it:
  - does not require all allowed repositories to be mentioned on the
    commandline instead, the (known) username is checked against the
    web configuration in the repository
  – allowes repository creation (by invoking hgadmin)


The hgadmin script serves several purposes, depending on the way it is

If called like 'hgadmin create_repo PATH' it will read the
configuration and create a repository.

If called like 'hgadmin updateauth' it will read its configuration,
and refresh access configuration; i.e. it will regenerate and update
the authorized_keys file and every repository configuration file, and
replace the htpasswd-file.

If called like 'hgadmin verify', it will read the configuration, and
exit successfully if the configuration is valid. If the configuration
is inconsistent, it will exit unsuccessfully.

If called like 'hgadmin accesscheck ACCESSTYPE USER PATH', the script
will read the configuration and check (and report) whether the user
USER is allowed access ACCESSTYPE to a (potentially not yet existing)
repository located at PATH.

All these invocations share these options:
  - "-D"            - enables debugging output
  - "--confdir DIR" - sets configuration directory to DIR

All files changed by hgadmin are first written to a temporary file,
then renamed to the real file. hgadmin does a fdatasync() before the
rename, which makes it kind of slow (~10 times slower). If you know
that fdatasync before rename is unnecessary on your filesystem,
comment out the fdatasync call. However, don't complain if you lose data...

The mkconfrepo is a helper script to create a new mercurial repository
that doubles as hgadmin configuration directory. It creates the above
mentioned configuration files, inserts some supposedly helpful
comments and commits them.

When started with the "--addserverhooks" or "--addclienthooks"
options, it will also add hooks that call hgadmin. The client hooks
call hgadmin with the "verify" parameter, to notify you of
configuration errors. The server hooks first update the repository to
the latest revision (tip) and then run hgadmin with "updateauth"