Page "Proposals/BEP-0003" was changed by olemis
Diff URL: 
<https://issues.apache.org/bloodhound/wiki/Proposals/BEP-0003?action=diff&version=35>
Revision 35
Comment: bep:0003 Remove details related to Routes framework
Changes:
-------8<------8<------8<------8<------8<------8<------8<------8<--------
Index: Proposals/BEP-0003
=========================================================================
--- Proposals/BEP-0003 (version: 34)
+++ Proposals/BEP-0003 (version: 35)
@@ -58,7 +58,7 @@
 
 }}}
 
-Product environments will not be recursive , which means that the following 
statement will fail
+Product environments will not be recursive, which means that the following 
statement will fail
 
 {{{
 #!py
@@ -92,7 +92,7 @@
   Implementation notes
 }}} 
 
-In order to make this happen and yet avoid object explosion , some caching 
strategy (e.g. 
[http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used LRU cache] 
like [http://docs.python.org/dev/library/functools.html#functools.lru_cache 
functools.lru_cache] or equivalent) should be installed in place , maybe 
combined with [http://docs.python.org/2.7/library/weakref.html weak references] 
.
+In order to make this happen and yet avoid object explosion, some caching 
strategy (e.g. 
[http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used LRU cache] 
like [http://docs.python.org/dev/library/functools.html#functools.lru_cache 
functools.lru_cache] or equivalent) should be installed in place, maybe 
combined with [http://docs.python.org/2.7/library/weakref.html weak references] 
.
 
 }}}
 
@@ -152,64 +152,75 @@
 
 [=#product-env-idem] Remaining members of `trac.env.Environment` will be 
returned by product environments too.
 
-=== Product routes #routes
-
-[http://routes.groovie.org/ Routes] framework will be used to match URLs based 
on predefined patterns and use this information to dispatch requests addressed 
to product environments. This will introduce an explicit installation 
dependency with that framework. 
-
-In first place there will be an instance of `routes.mapper.Mapper`. It will 
contain the definitions of the routes used to access product environments and 
their resources. Some variables in routes definitions will have special meaning 
though.
-
-||= Variable =||= Purpose =||= Observations =||
-|| `controller` || ''Ignored'' ||  ||
-|| `action` || ''Ignored'' ||  ||
-|| `envname` || Target environment name || Only used in multiple environment 
mode to determine target environment folder. Ignored otherwise. ||
-|| `product` || Target product prefix || Represents  ||
-|| `path_info` || Path to product resource or view  || Since paths under 
product base URL will be used to access anything that could be considered as 
belonging to the product, route definitions often will include 
[http://routes.readthedocs.org/en/latest/setting_up.html#magic-path-info 
path_info] to shift `SCRIPT_NAME` and `PATH_INFO` ''WSGI'' variables as 
expected ||
-
-Considering the previous discussion ''Bloodhound'' will provide a modified 
copy of the main entry point for the web interface (i.e. the 
[http://www.python.org/dev/peps/pep-0333/ WSGI] function 
`trac.web.main.dispatch_request`) to make it aware of external routing 
information. It will be wrapped by a `routes.middleware.RoutesMiddleware` 
object responsible for route matching and ''WSGI'' environment updates. This 
fact is important to understand subsequent discussion taking place in this 
section.
-
-Aforementioned data will be considered to dispatch requests in addition to the 
existing ''WSGI'' environment variables (i.e. `trac.env_path`, 
`trac.env_parent_dir`, `trac.env_index_template`, `trac.template_vars`, 
`trac.base_url`, `trac.env_paths`) or the equivalent global variables (i.e. 
`TRAC_ENV`, `TRAC_ENV_PARENT_DIR`, `TRAC_ENV_INDEX_TEMPLATE`, 
`TRAC_TEMPLATE_VARS`, `TRAC_BASE_URL`). The updated copy of 
`trac.web.main.dispatch_request` will read routing information like shown below
-
-{{{
-#!py
-
-route = environ.get('routes.route')
-match = environ.get('wsgiorg.routing_args', (None, {}) )[1]
-
-}}}
-
-==== Environment lookup #env-dispatch
-
-As a result the following rules will be used to determine the environment 
performing subsequent request dispatching :
-
-  - If `match == {}` (equivalent to `route is None`)
-    then no route was matched, hence respond with a 
-    404 (''Not Found'') HTTP status code.
-  - ... else if `match['envname']` is empty then no 
-    specific environment was requested. 
-    * For multi-environment deployments 
-      (i.e. `TRAC_ENV_PARENT_DIR`) just render an 
-      environment index page.
-    * For single-environment deployments always 
-      instantiate configured environment.
-  - ... for multi-environment deployments, if 
-    `match['envname']` is not empty then 
-    instantiate the environment at the corresponding 
-    child folder of the parent directory.
-  - ... if `match['product']` is empty then the 
-    global environment found up to this point will dispatch the 
-    request
-  - ... else if `match['product']` is set then 
-    [#product-env-api instantiate the product environment]
-    for the corresponding product prefix. There will be
-    an instance of `trac.web.main.RequestDispatcher`
-    responsible for further request dispatching in that 
-    context.
-
-Sub-paths will be handled from there as usual.
+=== Web bootstrap handlers and hooks #hooks
+
+Web bootstrap handlers are needed to cope with flexible [#url-mapping URL 
mappings]. Their type contract is defined as follows:
+
+{{{#!py
+
+class BootstrapHandlerBase(object):
+    """Objects responsible for loading the target environment and
+    request objects used in subsequent dispatching. 
+    """
+    def open_environment(self, environ, start_response):
+        """Load and initialize target Trac environment involved in request
+        dispatching.
+
+        The following WSGI entries will also be present in `environ` dict:
+
+        ||= WSGI variable =||= Environment variable =||= Comment =||
+        || trac.env_path || TRAC_ENV || See wiki:TracModWSGI ||
+        || trac.env_parent_dir || TRAC_ENV_PARENT_DIR || See wiki:TracModWSGI||
+        || trac.env_index_template || TRAC_ENV_INDEX_TEMPLATE || See 
wiki:TracInterfaceCustomization ||
+        || trac.template_vars || TRAC_TEMPLATE_VARS || See 
wiki:TracInterfaceCustomization ||
+        || trac.locale ||  || Target locale ||
+        || trac.base_url || TRAC_BASE_URL || Trac base URL hint ||
+
+        This method may handle the request (e.g. render environment index page)
+        in case environment lookup yields void results. In that case it MUST 
+        invoke WSGI `write` callable returned by `start_response` and raise 
+        `trac.web.api.RequestDone` exception.
+
+        :param environ: WSGI environment dict
+        :param start_response: WSGI callback for starting the response
+        :throws RequestDone: if the request is fully processed while loading
+                             target environment e.g. environment index page
+        :throws EnvironmentError: if it is impossible to find a way to locate
+                                  target environment e.g. TRAC_ENV and 
+                                  TRAC_ENV_PARENT_DIR both missing
+        :throws Exception: any other exception will be processed by the caller 
+                           in order to send a generic error message back to
+                           the HTTP client
+        """
+        raise NotImplementedError("Must override method 'open_environment'")
+
+    def create_request(self, env, environ, start_response):
+        """Instantiate request object used in subsequent request dispatching
+        
+        :param env: target Trac environment returned by `open_environment`
+        :param environ: WSGI environment dict
+        :param start_response: WSGI callback for starting the response
+        """
+        raise NotImplementedError("Must override method 'create_request'")
+
+}}}
+
+{{{#div class="well"
+
+{{{#!span class="label label-info"
+Note
+}}}
+
+[attachment:t514_r1477773_tracd_bootstrap.diff:ticket:514 A patch] has been 
proposed in #514 to
+augment the web bootstrap handlers protocol by adding `probe_environment` 
method, since it's needed
+to make TracStandalone authentication middlewares work under the new 
conditions.
+}}}
+
+Default bootstrap handler is a façade to underlying environment and request 
factories (i.e. hooks) which in turn by default [#deploy-global-env-embed embed 
product URL namespace into global namespace]. Other web bootstrap handlers may 
be contributed by third party libraries e.g. those implementing 
([#reject-routes rejected]) [./Routes Routes dispatching]. In order to select a 
given handler set either `trac.bootstrap_handler` WSGI variable or 
`TRAC_BOOTSTRAP_HANDLER` environment variable to an [#ref3 entry point 
expression] pointing to the target instance.
 
 === Resource neighborhoods #resource-neighborhood
 
-''Apache™ Bloodhound'' will expand resources API by introducing the concept of 
''Neighborhoods''. In a few words they are the topmost level of the hierarchy 
representing resources managed by a component manager, thereby identifying the 
later. As such ''resource neighborhoods'' serve to the purpose of specifying 
absolute references to resources hosted beyond the boundaries of a given 
component manager (e.g. ''Trac'' environments , [#product-envs product 
environments], ...) . As a side effect they will be used to load manager 
objects on demand.
+''Apache™ Bloodhound'' will expand resources API by introducing the concept of 
''Neighborhoods''. In a few words they are the topmost level of the hierarchy 
representing resources managed by a component manager, thereby identifying the 
later. As such ''resource neighborhoods'' serve to the purpose of specifying 
absolute references to resources hosted beyond the boundaries of a given 
component manager (e.g. ''Trac'' environments, [#product-envs product 
environments], ...) . As a side effect they will be used to load manager 
objects on demand.
 
 '''Resource neighborhoods''' will always be at the top of the resources 
hierarchy . Instances will have a ''realm'' identifying the kind of manager 
(e.g. `global` for ''Trac'' environment, `product` for product environments). 
For example the following resource identifiers will point to 
`attachment:file.txt:ticket:1` resource in local scope, product `P1`, product 
`P2` and global environment, in that order.
 
@@ -325,7 +336,7 @@
 
 Implementation notes
 }}}
-The code of Trac components structures file-system access using paths relative 
to the environment's top-level folder . Considering the fact that plugins 
migration towards multi-product support should be as smooth as possible the 
layout of product file-system area will be exactly the same as before . In 
other words , if the global environment's root folder is `/path/to/env` then 
the root folder of file-system area for product (prefix) `P` will be 
`/path/to/env/products/P` . Attachment `foo.txt` for ticket `42` in product `P` 
will be located at 
`/path/to/env/products/P/files/attachments/ticket/92c/92cfceb39d57d914ed8b14d0e37643de0797ae56/9206ac42b532ef8e983470c251f4e1a365fd636c.txt`
 whereas attachment `foo.txt` for ticket `42` in the global environment will be 
located at 
`/path/to/env/files/attachments/ticket/92c/92cfceb39d57d914ed8b14d0e37643de0797ae56/9206ac42b532ef8e983470c251f4e1a365fd636c.txt`.
+The code of Trac components structures file-system access using paths relative 
to the environment's top-level folder . Considering the fact that plugins 
migration towards multi-product support should be as smooth as possible the 
layout of product file-system area will be exactly the same as before . In 
other words, if the global environment's root folder is `/path/to/env` then the 
root folder of file-system area for product (prefix) `P` will be 
`/path/to/env/products/P` . Attachment `foo.txt` for ticket `42` in product `P` 
will be located at 
`/path/to/env/products/P/files/attachments/ticket/92c/92cfceb39d57d914ed8b14d0e37643de0797ae56/9206ac42b532ef8e983470c251f4e1a365fd636c.txt`
 whereas attachment `foo.txt` for ticket `42` in the global environment will be 
located at 
`/path/to/env/files/attachments/ticket/92c/92cfceb39d57d914ed8b14d0e37643de0797ae56/9206ac42b532ef8e983470c251f4e1a365fd636c.txt`.
 
 Product-aware components may do a different thing if they need to .
 
@@ -375,10 +386,10 @@
 
 `trac.conf.Configuration` instance in [#product-env-config config attribute] 
of product environments will read settings from the database and will be 
configured to inherit settings in environments' `trac.ini` file.
 
-Even if the proposal only suggests one database backend to store 
product-specific configuration it is also possible to think of using other 
configuration repositories (e.g. ''ini files'' , 
[http://hadoop.apache.org/hdfs/ HDFS], remote data repositories). It is a 
feasible enhancement though details have been omitted.
-}}}
-
-The following requirements are a corollary , considering that such 
customizations are performed in  the configuration file.
+Even if the proposal only suggests one database backend to store 
product-specific configuration it is also possible to think of using other 
configuration repositories (e.g. ''ini files'', [http://hadoop.apache.org/hdfs/ 
HDFS], remote data repositories). It is a feasible enhancement though details 
have been omitted.
+}}}
+
+The following requirements are a corollary, considering that such 
customizations are performed in  the configuration file.
 
 ==== Product logo #logo
 
@@ -455,109 +466,22 @@
 
 === Product resources namespaces #url-mapping
 
-Product and resource ID should form a two dimensional namespace. The mapping 
should be flexible enough to support the scenarios mentioned below. Other 
deployments may still be possible but are beyond the scope of this 
specification. However , in all cases every requests sent to any path under 
product base URL will be handled by the request handlers and filters activated 
in the target product environment.
+Product and resource ID should form a two dimensional namespace. The mapping 
should be flexible enough to support the scenarios mentioned below. Other 
deployments may still be possible but are beyond the scope of this 
specification. However, in all cases every requests sent to any path under 
product base URL will be handled by the request handlers and filters activated 
in the target product environment.
 
   - [=#deploy-domain] '''Product sub domain''' : The base URL of products
     will be at sibling sub-domains. A well-known
-    example is edgewall.org (i.e. bitten.edgewall.org ,
-    genshi.edgewall.org , trac.edgewall.org ). 
+    example is edgewall.org (i.e. bitten.edgewall.org,
+    genshi.edgewall.org, trac.edgewall.org ). 
     The global environment may be accessible at one 
     such sub-domain, at top level domain, or
     somewhere else.
-
-{{{
-#!div class="well"
-
-{{{
-#!span class="label label-info"
-
-  Implementation notes
-}}}
-
-'''TODO'''
-}}}
-
   - [=#deploy-sibling-paths] '''Product path namespace''' : Each product is 
     accessible at sibling paths on the same domain.
     The global environment will be available either at 
     one such path or somewhere else.
-
-{{{
-#!div class="well"
-
-{{{
-#!span class="label label-info"
-
-  Implementation notes
-}}} 
-
-This case is very similar to the [./MultienvParentDir reference 
multi-environment setup] .
-
-Considering the [#routes enhanced routing mechanism] the following routes 
definition will be suitable for single-environment installations
-
-{{{
-#!py
-
->>> from wsgiref.util import setup_testing_defaults as wsgi_environ
->>> from routes import Mapper
->>> m = Mapper()
->>> m.connect(None, '/path/to/bloodhound/{product}/{path_info:.*}')
->>> m.match('/path/to/bloodhound/product1/ticket/1')
-{'action': u'index', 'product': u'product1', 'controller': u'content', 
'path_info': 'ticket/1'}
-
-}}}
-
-If the global environment will be treated as yet another sibling product 
environment
-
-{{{
-#!py
-
->>> from wsgiref.util import setup_testing_defaults as wsgi_environ
->>> from routes import Mapper
->>> m = Mapper()
->>> m.connect(None, '/path/to/bloodhound/global/{path_info:.*}', product='')
->>> m.connect(None, '/path/to/bloodhound/{product}/{path_info:.*}')
->>> m.match('/path/to/bloodhound/global/ticket/1')
-{'action': u'index', 'product': u'', 'controller': u'content', 'path_info': 
'ticket/1'}
->>> m.match('/path/to/bloodhound/product1/ticket/1')
-{'action': u'index', 'product': u'product1', 'controller': u'content', 
'path_info': 'ticket/1'}
-
-}}}
-
-For multi-environment installations
-
-{{{
-#!py
-
->>> from wsgiref.util import setup_testing_defaults as wsgi_environ
->>> from routes import Mapper
->>> m = Mapper()
->>> m.connect(None, '/path/to/bloodhound/{envname}/{product}/{path_info:.*}')
->>> m.match('/path/to/bloodhound/enterprise/product1/ticket/1')
-{'action': u'index', 'product': u'product1', 'envname': u'enterprise', 
'controller': u'content', 'path_info': 'ticket/1'}
-
-}}}
-
-}}}
-
   - [=#deploy-global-env-embed] '''Embedded product path namespace''' : Each 
     product is accessible at sibling paths inside 
     the URL space of the global environment. 
-
-{{{
-#!div class="well"
-
-{{{
-#!span class="label label-info"
-
-  Implementation notes
-}}} 
-Even if this case is very similar to the [./MultienvParentDir reference 
multi-environment setup] a better match will be another setup based on 
[TracHacks:TracWsgiPlugin]. The main difference with respect to 
[#deploy-sibling-paths sibling paths] deployment is that the global environment 
plays an active role dispatching the requests addressed to an specific product 
by forwarding them to the corresponding product environment.
-
-This particular case does not even need to make use of the [#routes enhanced 
routing mechanism]. Instead of external routes definitions , a component 
(request handler) will be enabled in the global environment. It will handle 
requests sent to sub-paths of the form `product/<product 
prefix>/path/to/resource` relative to the global environment's base URL. 
Products base URL will be `product/<product prefix>` as a result. In its 
`process_request` method it will forward request dispatching to the instance of 
`trac.web.main.RequestDispatcher` of the corresponding product environment.
-
-}}}
-
   - [=#deploy-domain-path] '''Product sub domain + path namespace''' : 
     In this case product prefix will be 
     matched against URL sub domain whereas 
@@ -567,20 +491,6 @@
     web applications for projects . In general 
     product base URL will look like 
     `http://<product prefix>.domain.tld/path/to/bloodhound` .
-
-{{{
-#!div class="well"
-
-{{{
-#!span class="label label-info"
-
-  Implementation notes
-}}} 
-
-'''TODO'''
-
-}}}
-
   - [=#deploy-complex-path] '''Arbitrary product path namespace''' : 
     Sometimes hosting providers like forges do not 
     support product sub-domains . Hence 
@@ -590,63 +500,9 @@
     One such hypothetical example would be 
     `http://sourceforge.net/p/<product prefix>/bloodhound` .
 
-
-{{{
-#!div class="well"
-
-{{{
-#!span class="label label-info"
-
-  Implementation notes
-}}} 
-
-Considering the [#routes enhanced routing mechanism] the following routes 
definition will be suitable for single-environment installations
-
-{{{
-#!py
-
->>> from wsgiref.util import setup_testing_defaults as wsgi_environ
->>> from routes import Mapper
->>> m = Mapper()
->>> m.connect(None, 
'/path/to/bloodhound/{product}/path/to/bloodhound/{path_info:.*}')
->>> m.match('/path/to/bloodhound/product1/path/to/bloodhound/ticket/1')
-{'action': u'index', 'product': u'product1', 'controller': u'content', 
'path_info': 'ticket/1'}
-
-}}}
-
-The example mentioned above for ''Allura'' 
-
-{{{
-#!py
-
->>> from wsgiref.util import setup_testing_defaults as wsgi_environ
->>> from routes import Mapper
->>> m = Mapper()
->>> m.connect(None, '/p/{product}/bloodhound/{path_info:.*}')
->>> m.match('/p/product1/bloodhound/ticket/1')
-{'action': u'index', 'product': u'product1', 'controller': u'content', 
'path_info': 'ticket/1'}
-
-}}}
-
-For multi-environment installations
-
-{{{
-#!py
-
->>> from wsgiref.util import setup_testing_defaults as wsgi_environ
->>> from routes import Mapper
->>> m = Mapper()
->>> m.connect(None, 
'/{envname}/path/to/bloodhound/{product}/path/to/bloodhound/{path_info:.*}')
->>> m.match('/company/path/to/bloodhound/product1/path/to/bloodhound/ticket/1')
-{'action': u'index', 'product': u'product1', 'envname': u'company', 
'controller': u'content', 'path_info': 'ticket/1'}
-
-}}}
-
-}}}
-
 '''FIXME''' also be addressable through the product URL namespace, namely 
/ticket/<product prefix>/<local ticket id>. 
 
-In a multi-product configuration product resources should not be accessed 
using current global URL scheme (i.e. 
/path/to/bloodhound/<environment>/<realm>/<id>). Since [#permissions products 
will have their own permissions schema] then requests handled by components in 
the context of the top-level environment will perform neither the right 
permissions checks nor even use appropriate settings , and so on ... The same 
will happen for resources moved between products . In general such requests 
should be redirected to a URL under the namespace of resource's product.
+In a multi-product configuration product resources should not be accessed 
using current global URL scheme (i.e. 
/path/to/bloodhound/<environment>/<realm>/<id>). Since [#permissions products 
will have their own permissions schema] then requests handled by components in 
the context of the top-level environment will perform neither the right 
permissions checks nor even use appropriate settings, and so on ... The same 
will happen for resources moved between products . In general such requests 
should be redirected to a URL under the namespace of resource's product.
 
 ==== Tickets #tickets-namespace
 
@@ -656,7 +512,7 @@
 
 === Ubiquitous access to resources #resource-refs
 
-Existing resource APIs have to be extended to work across product boundaries 
(or component manager, if the feature is analysed from a more abstract 
perspective) . While performing some computation (e.g. request handling, 
trac-admin command , ...) it will be possible to refer to resources managed by 
another product (i.e. in another scope) . Notable use cases are:
+Existing resource APIs have to be extended to work across product boundaries 
(or component manager, if the feature is analysed from a more abstract 
perspective) . While performing some computation (e.g. request handling, 
trac-admin command, ...) it will be possible to refer to resources managed by 
another product (i.e. in another scope) . Notable use cases are:
 
   - Rendering context
   - Permission checks and related assertions
@@ -738,7 +594,7 @@
 
   - It has an impact on code scattered all over Trac core and plugins
   - Even if all invocations performed by resource API clients will still work 
-    for local resources , they'll have to be modified to cope with external
+    for local resources, they'll have to be modified to cope with external
     references.
     * ... which represents a challenge when it comes to providing generic 
       helpers to render links in ''Genshi'' templates .
@@ -806,11 +662,15 @@
 
 }}}
 
+==== Product routes #reject-routes
+
+[=#routes] Former versions stated that [http://routes.groovie.org/ Routes] 
framework would be used to match URLs based on predefined patterns and use this 
information to dispatch requests addressed to product environments. This 
proposal was rejected to be distributed with mainstream Apache™ Bloodhound 
distribution considering the fact that it will introduce an explicit 
installation dependency with that framework. However related details are still 
documented in [./Routes routes appendix] for historical reasons and also 
because this approach will be provided by third-party libraries.
+
 == Backwards Compatibility #backwards-compatibility
 
 The solution has to be compatible with single product solution whenever 
possible in order to make possible smooth upgrade paths from previous 
installations. This is particularly important for plugins to work 
out-of-the-box under the new circumstances or at least to make easier the 
upgrade development process for hack authors.
 
-''Product environments'' in proposed multi-product configuration will act as 
the replacement for sibling environments under common parent directory in 
[./MultienvParentDir reference multi-environments setup]. They will ensure that 
the correct translations will be performed for requests addressed to resources 
owned by a product using the `trac.env.Environment` API as usual. Components 
will have access to it by reading `self.env` attribute (as usual !!!). All this 
means that from a component perspective the very same operations will be 
invoked , thus plugins adaptation to the new conditions will be rather smooth. 
If plugins do want to know whether they are acting on behalf of a product 
environment or the global environment then the following conditional statement 
would be enough
+''Product environments'' in proposed multi-product configuration will act as 
the replacement for sibling environments under common parent directory in 
[./MultienvParentDir reference multi-environments setup]. They will ensure that 
the correct translations will be performed for requests addressed to resources 
owned by a product using the `trac.env.Environment` API as usual. Components 
will have access to it by reading `self.env` attribute (as usual !!!). All this 
means that from a component perspective the very same operations will be 
invoked, thus plugins adaptation to the new conditions will be rather smooth. 
If plugins do want to know whether they are acting on behalf of a product 
environment or the global environment then the following conditional statement 
would be enough
 
 {{{
 #!py
@@ -850,9 +710,11 @@
 
 == References #references
 
-  1. Multi-project support (trac:ticket:130)
-  2. Multiple Projects within a Single Trac Environment
+  1. [=#ref1] Multi-project support [[BR]] (trac:ticket:130)
+  2. [=#ref2] Multiple Projects within a Single Trac Environment [[BR]] 
      (trac:wiki:TracMultipleProjects/SingleEnvironment)
+  3. [=#ref3] Setuptools dynamic discovery of services and plugins [[BR]] 
+     
(http://peak.telecommunity.com/DevCenter/setuptools#dynamic-discovery-of-services-and-plugins)
 
 == Copyright #copyright
 
-------8<------8<------8<------8<------8<------8<------8<------8<--------

--
Page URL: <https://issues.apache.org/bloodhound/wiki/Proposals/BEP-0003>
Apache Bloodhound <https://issues.apache.org/bloodhound/>
The Apache Bloodhound issue tracker

This is an automated message. Someone added your email address to be
notified of changes on 'Proposals/BEP-0003' page.
If it was not you, please report to .

Reply via email to